Merge branch 'master' into feature/player-ui
This commit is contained in:
commit
1b829e095b
14
CHANGELOG.md
14
CHANGELOG.md
@ -18,6 +18,20 @@
|
||||
|
||||
## v5.x (current major)
|
||||
|
||||
### v5.1.0
|
||||
|
||||
* Re-enable logger
|
||||
* Move aspect ratio autodetection to requestAnimationFrame
|
||||
|
||||
### v5.0.7
|
||||
|
||||
* Videos of square-ish aspect ratios on 1440p (and lower) resolutions now no longer get misaligned ([#162](https://github.com/tamius-han/ultrawidify/issues/162))
|
||||
* Alignment of featured videos on youtube channel page should now also be fixed
|
||||
|
||||
### v5.0.6
|
||||
* Added configuration for metaivi.com based on user feedback ([#160](https://github.com/tamius-han/ultrawidify/issues/160))
|
||||
* Removed ExtConfPatches for versions < 4.5.0, because nobody should be using a build of this extension that's over a year old
|
||||
|
||||
### v5.0.5
|
||||
|
||||
* improved UX a bit
|
||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ultrawidify",
|
||||
"version": "5.0.4",
|
||||
"version": "5.1.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ultrawidify",
|
||||
"version": "5.0.5",
|
||||
"version": "5.1.0",
|
||||
"description": "Aspect ratio fixer for youtube and other sites, with automatic aspect ratio detection. Supports ultrawide and other ratios.",
|
||||
"author": "Tamius Han <tamius.han@gmail.com>",
|
||||
"scripts": {
|
||||
|
@ -276,6 +276,8 @@ export default {
|
||||
this.logStringified = undefined;
|
||||
}
|
||||
this.$store.dispatch('uw-hide-logger');
|
||||
|
||||
this.showLoggerUi = false;
|
||||
},
|
||||
closePopupAndStopLogging() {
|
||||
Logger.saveConfig({...this.lastSettings, allowLogging: false});
|
||||
@ -319,6 +321,7 @@ export default {
|
||||
font-size: 14px !important;
|
||||
|
||||
box-sizing: border-box !important;
|
||||
pointer-events: auto !important;
|
||||
}
|
||||
|
||||
div {
|
||||
|
@ -7,391 +7,6 @@ import BrowserDetect from './BrowserDetect';
|
||||
|
||||
const ExtensionConfPatch = [
|
||||
{
|
||||
forVersion: '4.2.0',
|
||||
sites: {
|
||||
"old.reddit.com" : {
|
||||
type: 'testing',
|
||||
DOM: {
|
||||
player: {
|
||||
manual: true,
|
||||
useRelativeAncestor: false,
|
||||
querySelectors: '.reddit-video-player-root, .media-preview-content'
|
||||
}
|
||||
},
|
||||
css: '',
|
||||
},
|
||||
"www.reddit.com" : {
|
||||
type: 'testing',
|
||||
DOM: {
|
||||
player: {
|
||||
manual: true,
|
||||
useRelativeAncestor: false,
|
||||
querySelectors: '.reddit-video-player-root, .media-preview-content'
|
||||
}
|
||||
},
|
||||
css: '',
|
||||
},
|
||||
"www.youtube.com" : {
|
||||
DOM: {
|
||||
player: {
|
||||
manual: true,
|
||||
querySelectors: "#movie_player, #player",
|
||||
additionalCss: "",
|
||||
useRelativeAncestor: false,
|
||||
playerNodeCss: "",
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}, {
|
||||
forVersion: '4.2.3.1',
|
||||
sites: {
|
||||
"old.reddit.com" : {
|
||||
type: 'testing',
|
||||
DOM: {
|
||||
player: {
|
||||
manual: true,
|
||||
useRelativeAncestor: false,
|
||||
querySelectors: '.media-preview-content, .reddit-video-player-root'
|
||||
}
|
||||
},
|
||||
css: '',
|
||||
},
|
||||
"www.reddit.com" : {
|
||||
type: 'testing',
|
||||
DOM: {
|
||||
player: {
|
||||
manual: true,
|
||||
useRelativeAncestor: false,
|
||||
querySelectors: '.media-preview-content, .reddit-video-player-root'
|
||||
}
|
||||
},
|
||||
css: '',
|
||||
},
|
||||
"www.youtube.com" : {
|
||||
DOM: {
|
||||
player: {
|
||||
manual: true,
|
||||
querySelectors: "#movie_player, #player",
|
||||
additionalCss: "",
|
||||
useRelativeAncestor: false,
|
||||
playerNodeCss: "",
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}, {
|
||||
forVersion: '4.3.0',
|
||||
sites: {
|
||||
"old.reddit.com" : {
|
||||
type: 'testing',
|
||||
DOM: {
|
||||
player: {
|
||||
manual: false,
|
||||
useRelativeAncestor: false,
|
||||
querySelectors: '.reddit-video-player-root, .media-preview-content'
|
||||
}
|
||||
},
|
||||
css: 'video {\n width: 100% !important;\n height: 100% !important;\n}',
|
||||
},
|
||||
"www.reddit.com" : {
|
||||
type: 'testing',
|
||||
DOM: {
|
||||
player: {
|
||||
manual: false,
|
||||
useRelativeAncestor: false,
|
||||
querySelectors: '.reddit-video-player-root, .media-preview-content'
|
||||
}
|
||||
},
|
||||
css: 'video {\n width: 100% !important;\n height: 100% !important;\n}',
|
||||
},
|
||||
}
|
||||
}, {
|
||||
forVersion: '4.3.1.1',
|
||||
sites: {
|
||||
'www.twitch.tv': {
|
||||
DOM: {
|
||||
player: {
|
||||
manual: false,
|
||||
querySelectors: "",
|
||||
additionalCss: "",
|
||||
useRelativeAncestor: false,
|
||||
playerNodeCss: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
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,
|
||||
}
|
||||
}, {
|
||||
userAdded: 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,
|
||||
}
|
||||
}, {
|
||||
userAdded: 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
forVersion: '4.4.1.1',
|
||||
sites: {
|
||||
"www.disneyplus.com": {
|
||||
DOM: {
|
||||
player: {
|
||||
periodicallyRefreshPlayerElement: true,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}, {
|
||||
forVersion: '4.4.2',
|
||||
updateFn: (userOptions, defaultOptions) => {
|
||||
try {
|
||||
userOptions.actions.push(
|
||||
{
|
||||
name: 'Stretch source to 4:3',
|
||||
label: '4:3 stretch (src)',
|
||||
cmd: [{
|
||||
action: 'set-stretch',
|
||||
arg: StretchType.FixedSource,
|
||||
customArg: 1.33,
|
||||
}],
|
||||
scopes: {
|
||||
page: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
playerUi: {
|
||||
show: true,
|
||||
path: 'crop'
|
||||
}
|
||||
}, {
|
||||
name: 'Stretch source to 16:9',
|
||||
label: '16:9 stretch (src)',
|
||||
cmd: [{
|
||||
action: 'set-stretch',
|
||||
arg: StretchType.FixedSource,
|
||||
customArg: 1.77,
|
||||
}],
|
||||
scopes: {
|
||||
page: {
|
||||
show: true,
|
||||
}
|
||||
},
|
||||
playerUi: {
|
||||
show: true,
|
||||
path: 'crop'
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("PROBLEM APPLYING SETTINGS", e);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
forVersion: '4.4.3.1',
|
||||
sites: {
|
||||
"www.disneyplus.com": {
|
||||
mode: ExtensionMode.Enabled,
|
||||
autoar: ExtensionMode.Enabled,
|
||||
autoarFallback: ExtensionMode.Enabled,
|
||||
override: true, // ignore value localStorage in favour of this
|
||||
stretch: StretchType.Default,
|
||||
videoAlignment: VideoAlignmentType.Default,
|
||||
keyboardShortcutsEnabled: ExtensionMode.Default,
|
||||
DOM: {
|
||||
player: {
|
||||
periodicallyRefreshPlayerElement: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
forVersion: '4.4.7',
|
||||
updateFn: (userOptions, defaultOptions) => {
|
||||
if (!userOptions.sites['www.netflix.com'].DOM) {
|
||||
userOptions.sites['www.netflix.com']['DOM'] = {
|
||||
"player": {
|
||||
"manual": true,
|
||||
"querySelectors": ".VideoContainer",
|
||||
"additionalCss": "",
|
||||
"useRelativeAncestor": false,
|
||||
"playerNodeCss": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!userOptions.sites['www.disneyplus.com']) {
|
||||
userOptions.sites['www.disneyplus.com'] = {
|
||||
mode: ExtensionMode.Enabled,
|
||||
autoar: ExtensionMode.Enabled,
|
||||
override: false,
|
||||
type: 'community',
|
||||
stretch: StretchType.Default,
|
||||
videoAlignment: VideoAlignmentType.Default,
|
||||
keyboardShortcutsEnabled: ExtensionMode.Default,
|
||||
arPersistence: true, // persist aspect ratio between different videos
|
||||
autoarPreventConditions: { // prevents autoar on following conditions
|
||||
videoStyleString: { // if video style string thing does anything of what follows
|
||||
containsProperty: { // if video style string has any of these properties (listed as keys)
|
||||
'height': { // if 'height' property is present in style attribute, we prevent autoar from running
|
||||
allowedValues: [ // unless attribute is equal to anything in here. Optional.
|
||||
'100%'
|
||||
]
|
||||
}
|
||||
// 'width': true // this would prevent aard from running if <video> had a 'width' property in style, regardless of value
|
||||
// could also be an empty object, in theory.
|
||||
}
|
||||
}
|
||||
},
|
||||
DOM: {
|
||||
"player": {
|
||||
"manual": true,
|
||||
"querySelectors": ".btn-media-clients",
|
||||
"additionalCss": "",
|
||||
"useRelativeAncestor": false,
|
||||
"playerNodeCss": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userOptions.sites['wwww.disneyplus.com']['DOM'] = {
|
||||
"player": {
|
||||
"manual": true,
|
||||
"querySelectors": ".btn-media-clients",
|
||||
"additionalCss": "",
|
||||
"useRelativeAncestor": false,
|
||||
"playerNodeCss": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
forVersion: '4.4.9',
|
||||
sites: {
|
||||
"www.youtube.com": {
|
||||
override: true,
|
||||
DOM: {
|
||||
player: {
|
||||
manual: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
forVersion: '4.5.0',
|
||||
sites: {
|
||||
"www.wakanim.tv": {
|
||||
@ -517,7 +132,7 @@ const ExtensionConfPatch = [
|
||||
"app.plex.tv": {
|
||||
mode: 3,
|
||||
autoar: 3,
|
||||
type: "user-added",
|
||||
type: "community",
|
||||
stretch: -1,
|
||||
videoAlignment: -1,
|
||||
keyboardShortcutsEnabled: 0,
|
||||
@ -533,6 +148,92 @@ const ExtensionConfPatch = [
|
||||
css: "body {\n background-color: #000;\n}\n\n.application {\n background-color: #000;\n}"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
forVersion: '5.0.6',
|
||||
sites: {
|
||||
"metaivi.com": {
|
||||
mode: 0,
|
||||
autoar: 0,
|
||||
type: "community",
|
||||
stretch: -1,
|
||||
videoAlignment: -1,
|
||||
DOM: {
|
||||
video: {
|
||||
manual: false,
|
||||
querySelectors: "",
|
||||
additionalCss: "position: absolute !important;"
|
||||
},
|
||||
player: {
|
||||
manual: false,
|
||||
querySelectors: "",
|
||||
additionalCss: "",
|
||||
useRelativeAncestor: false,
|
||||
playerNodeCss: ""
|
||||
}
|
||||
},
|
||||
"css": ""
|
||||
},
|
||||
"piped.kavin.rocks": {
|
||||
mode: 0,
|
||||
autoar: 0,
|
||||
type: 'community',
|
||||
autoarFallback: 0,
|
||||
stretch: 0,
|
||||
videoAlignment: -1,
|
||||
keyboardShortcutsEnabled: 0,
|
||||
DOM: {
|
||||
player: {
|
||||
manual: false,
|
||||
querySelectors: "",
|
||||
additionalCss: "",
|
||||
useRelativeAncestor: false,
|
||||
playerNodeCss: ""
|
||||
}
|
||||
},
|
||||
css: ".shaka-video-container {\n flex-direction: column !important;\n}"
|
||||
},
|
||||
},
|
||||
updateFn: (userOptions, defaultOptions) => {
|
||||
// 5.0.5 initially incorrectly had app.plex.tv marked as 'user-added'
|
||||
// when 'user-added' is generally reserved for marking sites with user-
|
||||
// changed configuration. Site patches submitted by community should have
|
||||
// 'community' type. extConfPatch for 5.0.5 was also retroactively corrected.
|
||||
userOptions.sites['app.plex.tv'].type = 'community';
|
||||
userOptions.sites['piped.kavin.rocks'] = {
|
||||
mode: 0,
|
||||
autoar: 0,
|
||||
type: 'community',
|
||||
autoarFallback: 0,
|
||||
stretch: 0,
|
||||
videoAlignment: -1,
|
||||
keyboardShortcutsEnabled: 0,
|
||||
DOM: {
|
||||
player: {
|
||||
manual: false,
|
||||
querySelectors: "",
|
||||
additionalCss: "",
|
||||
useRelativeAncestor: false,
|
||||
playerNodeCss: ""
|
||||
}
|
||||
},
|
||||
css: ".shaka-video-container {\n flex-direction: column !important;\n}"
|
||||
};
|
||||
}
|
||||
}, {
|
||||
forVersion: '5.0.7',
|
||||
updateFn: (userOptions, defaultOptions) => {
|
||||
// 5.0.5 initially incorrectly had app.plex.tv marked as 'user-added'
|
||||
// when 'user-added' is generally reserved for marking sites with user-
|
||||
// changed configuration. Site patches submitted by community should have
|
||||
// 'community' type. extConfPatch for 5.0.5 was also retroactively corrected.
|
||||
userOptions.sites['www.youtube.com'].DOM.player = {
|
||||
manual: true,
|
||||
querySelectors: "#movie_player, #player, #c4-player",
|
||||
additionalCss: "",
|
||||
useRelativeAncestor: false,
|
||||
playerNodeCss: "",
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -1031,8 +1031,8 @@ const ExtensionConf: SettingsInterface = {
|
||||
keyboardShortcutsEnabled: ExtensionMode.Default,
|
||||
DOM: {
|
||||
player: {
|
||||
manual: false,
|
||||
querySelectors: "#movie_player, #player",
|
||||
manual: true,
|
||||
querySelectors: "#movie_player, #player, #c4-player",
|
||||
additionalCss: "",
|
||||
useRelativeAncestor: false,
|
||||
playerNodeCss: "",
|
||||
@ -1195,7 +1195,7 @@ const ExtensionConf: SettingsInterface = {
|
||||
"app.plex.tv": {
|
||||
mode: 3,
|
||||
autoar: 3,
|
||||
type: "user-added",
|
||||
type: "community",
|
||||
stretch: -1,
|
||||
videoAlignment: -1,
|
||||
keyboardShortcutsEnabled: 0,
|
||||
@ -1209,7 +1209,48 @@ const ExtensionConf: SettingsInterface = {
|
||||
}
|
||||
},
|
||||
css: "body {\n background-color: #000;\n}\n\n.application {\n background-color: #000;\n}"
|
||||
}
|
||||
},
|
||||
"metaivi.com": {
|
||||
mode: 0,
|
||||
autoar: 0,
|
||||
type: "community",
|
||||
stretch: -1,
|
||||
videoAlignment: -1,
|
||||
DOM: {
|
||||
video: {
|
||||
manual: false,
|
||||
querySelectors: "",
|
||||
additionalCss: "position: absolute !important;"
|
||||
},
|
||||
player: {
|
||||
manual: false,
|
||||
querySelectors: "",
|
||||
additionalCss: "",
|
||||
useRelativeAncestor: false,
|
||||
playerNodeCss: ""
|
||||
}
|
||||
},
|
||||
"css": ""
|
||||
},
|
||||
"piped.kavin.rocks": {
|
||||
mode: 0,
|
||||
autoar: 0,
|
||||
type: 'community',
|
||||
autoarFallback: 0,
|
||||
stretch: 0,
|
||||
videoAlignment: -1,
|
||||
keyboardShortcutsEnabled: 0,
|
||||
DOM: {
|
||||
player: {
|
||||
manual: false,
|
||||
querySelectors: "",
|
||||
additionalCss: "",
|
||||
useRelativeAncestor: false,
|
||||
playerNodeCss: ""
|
||||
}
|
||||
},
|
||||
css: ".shaka-video-container {\n flex-direction: column !important;\n}"
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -314,23 +314,31 @@ 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(event)) {
|
||||
this.logger.log('info', 'keyboard', "[ActionHandler::handleKeyup] we are in a text box or something. Doing nothing.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (this.preventAction(event)) {
|
||||
this.logger.log('info', 'keyboard', "[ActionHandler::handleKeyup] we are in a text box or something. Doing nothing.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.execAction(this.keyUpActions, event);
|
||||
this.execAction(this.keyUpActions, event);
|
||||
} catch (e) {
|
||||
this.logger.log('info', 'debug', '[ActionHandler::handleKeyup] Failed to handle keyup!', e);
|
||||
}
|
||||
}
|
||||
|
||||
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(event)) {
|
||||
this.logger.log('info', 'keyboard', "[ActionHandler::handleKeydown] we are in a text box or something. Doing nothing.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (this.preventAction(event)) {
|
||||
this.logger.log('info', 'keyboard', "[ActionHandler::handleKeydown] we are in a text box or something. Doing nothing.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.execAction(this.keyDownActions, event);
|
||||
this.execAction(this.keyDownActions, event);
|
||||
} catch (e) {
|
||||
this.logger.log('info', 'debug', '[ActionHandler::handleKeydown] Failed to handle keydown!', e);
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseMove(event, videoData?: VideoData) {
|
||||
|
@ -91,6 +91,10 @@ class Logger {
|
||||
constructor(options?: {vuexStore?: any, uwInstance?: any}) {
|
||||
this.vuexStore = options?.vuexStore;
|
||||
this.uwInstance = options?.uwInstance;
|
||||
|
||||
browser.storage.onChanged.addListener((changes, area) => {
|
||||
this.storageChangeListener(changes, area)
|
||||
});
|
||||
}
|
||||
|
||||
static saveConfig(conf: LoggerConfig) {
|
||||
@ -190,6 +194,34 @@ class Logger {
|
||||
});
|
||||
}
|
||||
|
||||
storageChangeListener(changes, area) {
|
||||
if (!changes.uwLogger) {
|
||||
console.info('We dont have any logging settings, not processing frther');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.conf = JSON.parse(changes.uwLogger.newValue);
|
||||
} catch (e) {
|
||||
console.warn('[uwLogger] Error while trying to parse new conf for logger:', e, '\nWe received the following changes:', changes, 'for area:', area);
|
||||
}
|
||||
|
||||
// This code can only execute if user tried to enable or disable logging
|
||||
// through the popup. In cases like this, we do not gate the console.log
|
||||
// behind a check, since we _always_ want to have this feedback in response
|
||||
// to an action.
|
||||
console.info(
|
||||
'[uwLogger] logger config changed! New configuration:',
|
||||
this.conf, '\nraw changes:', changes, 'area?', area,
|
||||
'\n————————————————————————————————————————————————————————————————————————\n\n\n\n\n\n\n\n\n\n\n\n-----\nLogging with new settings starts now.'
|
||||
);
|
||||
|
||||
// initiate loger if need be
|
||||
if (!this.startTime) {
|
||||
this.init(this.conf);
|
||||
}
|
||||
}
|
||||
|
||||
setVuexStore(store) {
|
||||
this.vuexStore = store;
|
||||
}
|
||||
@ -386,7 +418,7 @@ class Logger {
|
||||
}
|
||||
|
||||
canLogFile(component) {
|
||||
if (!this.conf.fileOptions.enabled || this.temp_disable) {
|
||||
if (!this.conf?.fileOptions?.enabled || this.temp_disable) {
|
||||
return false;
|
||||
}
|
||||
if (Array.isArray(component) && component.length ) {
|
||||
|
@ -14,6 +14,13 @@ import Logger from '../Logger';
|
||||
import VideoData from '../video-data/VideoData';
|
||||
import Settings from '../Settings';
|
||||
|
||||
enum VideoPlaybackState {
|
||||
Playing,
|
||||
Paused,
|
||||
Ended,
|
||||
Error
|
||||
}
|
||||
|
||||
class ArDetector {
|
||||
logger: Logger;
|
||||
conf: VideoData;
|
||||
@ -44,6 +51,7 @@ class ArDetector {
|
||||
canDoFallbackMode: boolean = false;
|
||||
|
||||
// helper objects
|
||||
private animationFrameHandle: any;
|
||||
private attachedCanvas: HTMLCanvasElement;
|
||||
canvas: HTMLCanvasElement;
|
||||
private blackframeCanvas: HTMLCanvasElement;
|
||||
@ -56,7 +64,83 @@ class ArDetector {
|
||||
private detectedAr: any;
|
||||
private canvasDrawWindowHOffset: number;
|
||||
private sampleCols_current: number;
|
||||
private timers = {
|
||||
nextFrameCheckTime: Date.now()
|
||||
}
|
||||
private status = {
|
||||
lastVideoStatus: VideoPlaybackState.Playing
|
||||
}
|
||||
|
||||
//#region debug variables
|
||||
private performanceConfig = {
|
||||
sampleCountForAverages: 32
|
||||
}
|
||||
private performance = {
|
||||
animationFrame: {
|
||||
lastTime: 0,
|
||||
currentIndex: 0,
|
||||
sampleTime: []
|
||||
},
|
||||
drawImage: {
|
||||
currentIndex: 0,
|
||||
sampleTime: [],
|
||||
},
|
||||
getImageData: {
|
||||
currentIndex: 0,
|
||||
sampleTime: [],
|
||||
},
|
||||
aard: {
|
||||
currentIndex: 0,
|
||||
sampleTime: [],
|
||||
}
|
||||
};
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region getters
|
||||
get defaultAr() {
|
||||
const ratio = this.video.videoWidth / this.video.videoHeight;
|
||||
if (isNaN(ratio)) {
|
||||
return undefined;
|
||||
}
|
||||
return ratio;
|
||||
}
|
||||
|
||||
//#endregion getters
|
||||
|
||||
//#region debug getters
|
||||
|
||||
/**
|
||||
* We get one animation frame per this many ms. This means that our autodetection
|
||||
* stuff must run in less than this many ms. This valuz is averaged out over multiple
|
||||
* samples for better accuracy.
|
||||
*
|
||||
* Returns value in ms.
|
||||
*
|
||||
* A very important caveat: if autodetection takes up too much time, it WILL artificially
|
||||
* increase time budget. Therefore, you should use (and firstly even implement) getTimeBudget()
|
||||
* that turns off autodetection for a second or so to gather accurate timing info.
|
||||
*/
|
||||
get eyeballedTimeBudget() {
|
||||
let sum;
|
||||
for (let i = 0; i < this.performance.animationFrame.sampleTime.length; i++) {
|
||||
sum += this.performance.animationFrame.sampleTime[i];
|
||||
}
|
||||
|
||||
return sum / this.performance.animationFrame.sampleTime.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts time budget (eyeballed) into actual framerate. Since eyeballed time budget rises
|
||||
* if our autodetection takes too long, it's still good enough for calculating framerate
|
||||
*/
|
||||
get fps() {
|
||||
return 1000 / this.eyeballedTimeBudget;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region lifecycle
|
||||
constructor(videoData){
|
||||
this.logger = videoData.logger;
|
||||
this.conf = videoData;
|
||||
@ -73,14 +157,6 @@ class ArDetector {
|
||||
this.logger.log('info', 'init', `[ArDetector::ctor] creating new ArDetector. arid: ${this.arid}`);
|
||||
}
|
||||
|
||||
setManualTick(manualTick) {
|
||||
this.manualTickEnabled = manualTick;
|
||||
}
|
||||
|
||||
tick() {
|
||||
this._nextTick = true;
|
||||
}
|
||||
|
||||
init(){
|
||||
this.logger.log('info', 'init', `[ArDetect::init] <@${this.arid}> Initializing autodetection.`);
|
||||
|
||||
@ -95,12 +171,6 @@ class ArDetector {
|
||||
}
|
||||
}
|
||||
|
||||
destroy(){
|
||||
this.logger.log('info', 'init', `%c[ArDetect::destroy] <@${this.arid}> Destroying aard.`, _ard_console_stop);
|
||||
// this.debugCanvas.destroy();
|
||||
this.stop();
|
||||
}
|
||||
|
||||
setup(cwidth?: number, cheight?: number){
|
||||
this.logger.log('info', 'init', `[ArDetect::setup] <@${this.arid}> Starting autodetection setup.`);
|
||||
//
|
||||
@ -215,12 +285,13 @@ class ArDetector {
|
||||
this.resetBlackLevel();
|
||||
|
||||
// if we're restarting ArDetect, we need to do this in order to force-recalculate aspect ratio
|
||||
this.conf.resizer.setLastAr({type: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
|
||||
this.conf.resizer.setLastAr({type: AspectRatioType.Automatic, ratio: this.defaultAr});
|
||||
|
||||
this.canvasImageDataRowLength = cwidth << 2;
|
||||
this.noLetterboxCanvasReset = false;
|
||||
|
||||
if (this.settings.canStartAutoAr() ) {
|
||||
// this.main();
|
||||
this.start();
|
||||
}
|
||||
|
||||
@ -232,6 +303,14 @@ class ArDetector {
|
||||
this.conf.arSetupComplete = true;
|
||||
}
|
||||
|
||||
destroy(){
|
||||
this.logger.log('info', 'init', `%c[ArDetect::destroy] <@${this.arid}> Destroying aard.`, _ard_console_stop);
|
||||
// this.debugCanvas.destroy();
|
||||
this.halt();
|
||||
}
|
||||
//#endregion lifecycle
|
||||
|
||||
//#region AARD control
|
||||
start() {
|
||||
if (this.settings.canStartAutoAr()) {
|
||||
this.logger.log('info', 'debug', `"%c[ArDetect::start] <@${this.arid}> Starting automatic aspect ratio detection`, _ard_console_start);
|
||||
@ -242,27 +321,38 @@ class ArDetector {
|
||||
|
||||
if (this.conf.resizer.lastAr.type === AspectRatioType.Automatic) {
|
||||
// ensure first autodetection will run in any case
|
||||
this.conf.resizer.setLastAr({type: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
|
||||
this.conf.resizer.setLastAr({type: AspectRatioType.Automatic, ratio: this.defaultAr});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// launch main() if it's currently not running:
|
||||
this.main();
|
||||
// automatic detection starts halted. If halted=false when main first starts, extension won't run
|
||||
// this._paused is undefined the first time we run this function, which is effectively the same thing
|
||||
// as false. Still, we'll explicitly fix this here.
|
||||
this._paused = false;
|
||||
this._halted = false;
|
||||
this._paused = false;
|
||||
|
||||
// start autodetection
|
||||
this.startLoop();
|
||||
|
||||
// automatic detection starts halted. If halted=false when main first starts, extension won't run
|
||||
// this._paused is undefined the first time we run this function, which is effectively the same thing
|
||||
// as false. Still, we'll explicitly fix this here.
|
||||
}
|
||||
|
||||
startLoop() {
|
||||
if (this.animationFrameHandle) {
|
||||
window.cancelAnimationFrame(this.animationFrameHandle);
|
||||
}
|
||||
|
||||
this.animationFrameHandle = window.requestAnimationFrame( (ts) => this.animationFrameBootstrap(ts));
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.animationFrameHandle) {
|
||||
window.cancelAnimationFrame(this.animationFrameHandle);
|
||||
}
|
||||
this.logger.log('info', 'debug', `"%c[ArDetect::stop] <@${this.arid}> Stopping AnimationFrame loop.`, _ard_console_stop);
|
||||
}
|
||||
|
||||
unpause() {
|
||||
// pause only if we were running before. Don't pause if we aren't running
|
||||
// (we are running when _halted is neither true nor undefined)
|
||||
if (this._paused && this._halted === false) {
|
||||
this._paused = true;
|
||||
}
|
||||
this.startLoop();
|
||||
}
|
||||
|
||||
pause() {
|
||||
@ -271,108 +361,76 @@ class ArDetector {
|
||||
if (this._halted === false) {
|
||||
this._paused = true;
|
||||
}
|
||||
this.stop();
|
||||
}
|
||||
|
||||
stop(){
|
||||
this.logger.log('info', 'debug', `"%c[ArDetect::stop] <@${this.arid}> Stopping automatic aspect ratio detection`, _ard_console_stop);
|
||||
halt(){
|
||||
this.logger.log('info', 'debug', `"%c[ArDetect::stop] <@${this.arid}> Halting automatic aspect ratio detection`, _ard_console_stop);
|
||||
this.stop();
|
||||
this._halted = true;
|
||||
// this.conf.resizer.setArLastAr();
|
||||
}
|
||||
|
||||
async main() {
|
||||
if (this._paused) {
|
||||
// unpause if paused
|
||||
this._paused = false;
|
||||
return; // main loop still keeps executing. Return is needed to avoid a million instances of autodetection
|
||||
}
|
||||
if (!this._halted) {
|
||||
// we are already running, don't run twice
|
||||
// this would have handled the 'paused' from before, actually.
|
||||
return;
|
||||
}
|
||||
|
||||
let exitedRetries = 10;
|
||||
|
||||
while (!this._exited && exitedRetries --> 0) {
|
||||
this.logger.log('warn', 'debug', `[ArDetect::main] <@${this.arid}> We are trying to start another instance of autodetection on current video, but the previous instance hasn't exited yet. Waiting for old instance to exit ...`);
|
||||
await sleep(this.settings.active.arDetect.timers.tickrate);
|
||||
}
|
||||
if (!this._exited) {
|
||||
this.logger.log('error', 'debug', `[ArDetect::main] <@${this.arid}> Previous instance didn't exit in time. Not starting a new one.`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.log('info', 'debug', `%c[ArDetect::main] <@${this.arid}> Previous instance didn't exit in time. Not starting a new one.`);
|
||||
|
||||
// we need to unhalt:
|
||||
this._halted = false;
|
||||
this._exited = false;
|
||||
|
||||
// set initial timestamps so frame check will trigger the first time we run the loop
|
||||
let lastFrameCheckStartTime = Date.now() - (this.settings.active.arDetect.timers.playing << 1);
|
||||
|
||||
const frameCheckTimes = new Array(10).fill(-1);
|
||||
let frameCheckBufferIndex = 0;
|
||||
let fcstart, fctime;
|
||||
|
||||
while (this && !this._halted) {
|
||||
// NOTE: we separated tickrate and inter-check timeouts so that when video switches
|
||||
// state from 'paused' to 'playing', we don't need to wait for the rest of the longer
|
||||
// paused state timeout to finish.
|
||||
|
||||
if ( (!this.manualTickEnabled && this.canTriggerFrameCheck(lastFrameCheckStartTime)) || this._nextTick) {
|
||||
this._nextTick = false;
|
||||
|
||||
lastFrameCheckStartTime = Date.now();
|
||||
fcstart = performance.now();
|
||||
|
||||
try {
|
||||
this.frameCheck();
|
||||
} catch (e) {
|
||||
this.logger.log('error', 'debug', `%c[ArDetect::main] <@${this.arid}> Frame check failed:`, "color: #000, background: #f00", e);
|
||||
}
|
||||
|
||||
if (Debug.performanceMetrics) {
|
||||
fctime = performance.now() - fcstart;
|
||||
frameCheckTimes[frameCheckBufferIndex % frameCheckTimes.length] = fctime;
|
||||
this.conf.pageInfo.sendPerformanceUpdate({frameCheckTimes: frameCheckTimes, lastFrameCheckTime: fctime});
|
||||
++frameCheckBufferIndex;
|
||||
}
|
||||
}
|
||||
|
||||
await sleep(this.settings.active.arDetect.timers.tickrate);
|
||||
}
|
||||
|
||||
this.logger.log('info', 'debug', `%c[ArDetect::main] <@${this.arid}> Main autodetection loop exited. Halted? ${this._halted}`, _ard_console_stop);
|
||||
this._exited = true;
|
||||
setManualTick(manualTick) {
|
||||
this.manualTickEnabled = manualTick;
|
||||
}
|
||||
|
||||
|
||||
canTriggerFrameCheck(lastFrameCheckStartTime) {
|
||||
if (this._paused) {
|
||||
return false;
|
||||
}
|
||||
if (this.video.ended || this.video.paused){
|
||||
// we slow down if ended or pausing. Detecting is pointless.
|
||||
// we don't stop outright in case seeking happens during pause/after video was
|
||||
// ended and video gets into 'playing' state again
|
||||
return Date.now() - lastFrameCheckStartTime > this.settings.active.arDetect.timers.paused;
|
||||
}
|
||||
if (this.video.error){
|
||||
// če je video pavziran, še vedno skušamo zaznati razmerje stranic - ampak bolj poredko.
|
||||
// if the video is paused, we still do autodetection. We just do it less often.
|
||||
return Date.now() - lastFrameCheckStartTime > this.settings.active.arDetect.timers.error;
|
||||
}
|
||||
|
||||
return Date.now() - lastFrameCheckStartTime > this.settings.active.arDetect.timers.playing;
|
||||
tick() {
|
||||
this._nextTick = true;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region helper functions (general)
|
||||
|
||||
isRunning(){
|
||||
return ! (this._halted || this._paused || this._exited);
|
||||
}
|
||||
|
||||
private getVideoPlaybackState(): VideoPlaybackState {
|
||||
try {
|
||||
if (this.video.ended) {
|
||||
return VideoPlaybackState.Ended;
|
||||
} else if (this.video.paused) {
|
||||
return VideoPlaybackState.Paused;
|
||||
} else if (this.video.error) {
|
||||
return VideoPlaybackState.Error;
|
||||
} else {
|
||||
return VideoPlaybackState.Playing;
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.log('warn', 'debug', `[ArDetect::getVideoPlaybackState] There was an error while determining video playback state.`, e);
|
||||
return VideoPlaybackState.Error;
|
||||
}
|
||||
}
|
||||
|
||||
scheduleInitRestart(timeout?: number, force_reset?: boolean){
|
||||
/**
|
||||
* Checks whether conditions for granting a frame check are fulfilled
|
||||
* @returns
|
||||
*/
|
||||
private canTriggerFrameCheck() {
|
||||
if (this._paused) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if video was paused & we know that we already checked that frame,
|
||||
// we will not check it again.
|
||||
const videoState = this.getVideoPlaybackState();
|
||||
if (videoState !== VideoPlaybackState.Playing) {
|
||||
if (this.status.lastVideoStatus === videoState) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this.status.lastVideoStatus = videoState;
|
||||
|
||||
if (Date.now() < this.timers.nextFrameCheckTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.timers.nextFrameCheckTime = Date.now() + this.settings.active.arDetect.timers.playing;
|
||||
return true;
|
||||
}
|
||||
|
||||
private scheduleInitRestart(timeout?: number, force_reset?: boolean){
|
||||
if(! timeout){
|
||||
timeout = 100;
|
||||
}
|
||||
@ -385,7 +443,7 @@ class ArDetector {
|
||||
this.setupTimer = setTimeout(function(){
|
||||
ths.setupTimer = null;
|
||||
try{
|
||||
ths.main();
|
||||
ths.start();
|
||||
} catch(e) {
|
||||
this.logger('error', 'debug', `[ArDetector::scheduleInitRestart] <@${this.arid}> Failed to start main(). Error:`,e);
|
||||
}
|
||||
@ -395,10 +453,7 @@ class ArDetector {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//#region helper functions (general)
|
||||
attachCanvas(canvas){
|
||||
private attachCanvas(canvas){
|
||||
if(this.attachedCanvas)
|
||||
this.attachedCanvas.remove();
|
||||
|
||||
@ -412,25 +467,92 @@ class ArDetector {
|
||||
.appendChild(canvas);
|
||||
}
|
||||
|
||||
canvasReadyForDrawWindow(){
|
||||
private canvasReadyForDrawWindow(){
|
||||
this.logger.log('info', 'debug', `%c[ArDetect::canvasReadyForDrawWindow] <@${this.arid}> canvas is ${this.canvas.height === window.innerHeight ? '' : 'NOT '}ready for drawWindow(). Canvas height: ${this.canvas.height}px; window inner height: ${window.innerHeight}px.`)
|
||||
|
||||
return this.canvas.height == window.innerHeight
|
||||
}
|
||||
|
||||
getTimeout(baseTimeout, startTime){
|
||||
let execTime = (performance.now() - startTime);
|
||||
|
||||
return baseTimeout;
|
||||
/**
|
||||
* Adds execution time sample for performance metrics
|
||||
* @param performanceObject
|
||||
* @param executionTime
|
||||
*/
|
||||
private addPerformanceTimeMeasure(performanceObject, executionTime) {
|
||||
performanceObject.sampleTime[performanceObject.currentIndex] = executionTime;
|
||||
performanceObject.currentIndex = (performanceObject.currentIndex + 1) % this.performanceConfig.sampleCountForAverages;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region helper functions (performance measurements)
|
||||
|
||||
/**
|
||||
* Returns time ultrawidify spends on certain aspects of autodetection.
|
||||
*
|
||||
* The returned object contains the following:
|
||||
*
|
||||
* eyeballedTimeBudget — a very inaccurate time budget
|
||||
* fps — framerate at which we run
|
||||
* aardTime — time spent on average frameCheck loop.
|
||||
* It's a nearly useless metric, because
|
||||
* frameCheck can exit very early.
|
||||
* drawWindowTime — how much time browser spends on executing
|
||||
* drawWindow() calls.
|
||||
* getImageData — how much time browser spends on executing
|
||||
* getImageData() calls.
|
||||
*
|
||||
* Most of these are on "per frame" basis and averaged.
|
||||
*/
|
||||
getTimings() {
|
||||
let drawWindowTime = 0, getImageDataTime = 0, aardTime = 0;
|
||||
|
||||
// drawImage and getImageData are of same length and use same everything
|
||||
for (let i = 0; i < this.performance.drawImage.sampleTime.length; i++) {
|
||||
drawWindowTime += this.performance.drawImage.sampleTime[i] ?? 0;
|
||||
getImageDataTime += this.performance.getImageData.sampleTime[i] ?? 0;
|
||||
}
|
||||
drawWindowTime /= this.performance.drawImage.sampleTime.length;
|
||||
getImageDataTime /= this.performance.getImageData.sampleTime.length;
|
||||
|
||||
return {
|
||||
eyeballedTimeBudget: this.eyeballedTimeBudget,
|
||||
fps: this.fps,
|
||||
drawWindowTime,
|
||||
getImageDataTime
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
getDefaultAr() {
|
||||
const ratio = this.video.videoWidth / this.video.videoHeight;
|
||||
if (isNaN(ratio)) {
|
||||
return undefined;
|
||||
/**
|
||||
* This is the "main loop" for aspect ratio autodetection
|
||||
*/
|
||||
private animationFrameBootstrap(timestamp: number) {
|
||||
// do timekeeping first
|
||||
this.addPerformanceTimeMeasure(this.performance.animationFrame, timestamp - this.performance.animationFrame.lastTime)
|
||||
this.performance.animationFrame.lastTime = timestamp;
|
||||
|
||||
// trigger frame check, if we're allowed to
|
||||
if ( (!this.manualTickEnabled && this.canTriggerFrameCheck()) || this._nextTick) {
|
||||
this._nextTick = false;
|
||||
|
||||
try {
|
||||
const startTime = performance.now();
|
||||
this.frameCheck();
|
||||
this.addPerformanceTimeMeasure(this.performance.aard, performance.now() - startTime);
|
||||
} catch (e) {
|
||||
this.logger.log('error', 'debug', `%c[ArDetect::main] <@${this.arid}> Frame check failed:`, "color: #000, background: #f00", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (this && !this._halted && !this._paused) {
|
||||
this.animationFrameHandle = window.requestAnimationFrame( (ts) => this.animationFrameBootstrap(ts));
|
||||
} else if (this._halted) {
|
||||
this.logger.log('info', 'debug', `%c[ArDetect::main] <@${this.arid}> Main autodetection loop exited. Halted? ${this._halted}`, _ard_console_stop);
|
||||
this._exited = true;
|
||||
} else {
|
||||
this.logger.log('info', 'debug', `[ArDetect::main] <@${this.arid}> Not renewing animation frame for some reason. Paused? ${this._paused}; Halted?: ${this._halted}, Exited?: ${this._exited}`);
|
||||
}
|
||||
return ratio;
|
||||
}
|
||||
|
||||
calculateArFromEdges(edges) {
|
||||
@ -549,43 +671,33 @@ class ArDetector {
|
||||
this.init();
|
||||
}
|
||||
|
||||
let startTime = performance.now();
|
||||
let startTime;
|
||||
let partialDrawImageTime = 0;
|
||||
let sampleCols = this.sampleCols.slice(0);
|
||||
|
||||
//
|
||||
// [0] blackframe tests (they also determine whether we need fallback mode)
|
||||
//
|
||||
try {
|
||||
startTime = performance.now();
|
||||
this.blackframeContext.drawImage(this.video, 0, 0, this.blackframeCanvas.width, this.blackframeCanvas.height);
|
||||
partialDrawImageTime += performance.now() - startTime;
|
||||
|
||||
// special browsers require special tests
|
||||
// if (this.hasDRM()) {
|
||||
// this.fallbackMode = false;
|
||||
// throw 'VIDEO_DRM_PROTECTED';
|
||||
// }
|
||||
this.fallbackMode = false;
|
||||
|
||||
// If we detected DRM and if we're here, this means we're also using Google Chrome.
|
||||
// And if we're here while DRM is detected, we know we'll be looking at black frames.
|
||||
// We won't be able to do anything useful, therefore we're just gonna call it quits.
|
||||
if (this.conf.hasDrm) {
|
||||
this.logger.log('info', 'debug', 'we have drm, doing nothing.', this.conf.hasDrm);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.log('error', 'arDetect', `%c[ArDetect::frameCheck] <@${this.arid}> %c[ArDetect::frameCheck] can't draw image on canvas. ${this.canDoFallbackMode ? 'Trying canvas.drawWindow instead' : 'Doing nothing as browser doesn\'t support fallback mode.'}`, "color:#000; backgroud:#f51;", e);
|
||||
|
||||
// in case of DRM errors, we need to prevent the execution to reach the aspec
|
||||
// ratio setting part of this function. For the time being, we're only stopping
|
||||
// in case we encounter DRM error in Chrome. Firefox has fallback mode and generates
|
||||
// different error, so that goes.
|
||||
// if (e === 'VIDEO_DRM_PROTECTED') {
|
||||
// // nothing to see here, really, if fallback mode isn't supported by browser
|
||||
// if (!this.drmNotificationShown) {
|
||||
// this.drmNotificationShown = true;
|
||||
|
||||
// this.conf.player.showNotification('AARD_DRM');
|
||||
// this.conf.resizer.setAr({type: AspectRatio.Reset});
|
||||
// }
|
||||
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (! this.canvasReadyForDrawWindow()) {
|
||||
// this means canvas needs to be resized, so we'll just re-run setup with all those new parameters
|
||||
this.stop();
|
||||
this.halt();
|
||||
|
||||
let newCanvasWidth = window.innerHeight * (this.video.videoWidth / this.video.videoHeight);
|
||||
let newCanvasHeight = window.innerHeight;
|
||||
@ -606,14 +718,16 @@ class ArDetector {
|
||||
// smaller sample for blackframe check
|
||||
this.fallbackMode = true;
|
||||
|
||||
startTime = performance.now();
|
||||
try {
|
||||
(this.context as any).drawWindow(window, this.canvasDrawWindowHOffset, 0, this.canvas.width, this.canvas.height, "rgba(0,0,128,1)");
|
||||
(this.context as any).drawWindow(window, this.canvasDrawWindowHOffset, 0, this.canvas.width, this.canvas.height);
|
||||
} catch (e) {
|
||||
this.logger.log('error', 'arDetect', `%c[ArDetect::frameCheck] can't draw image on canvas with fallback mode either. This error is prolly only temporary.`, "color:#000; backgroud:#f51;", e);
|
||||
return; // it's prolly just a fluke, so we do nothing special here
|
||||
}
|
||||
// draw blackframe sample from our main sample:
|
||||
this.blackframeContext.drawImage(this.canvas, this.blackframeCanvas.width, this.blackframeCanvas.height)
|
||||
this.blackframeContext.drawImage(this.canvas, this.blackframeCanvas.width, this.blackframeCanvas.height);
|
||||
partialDrawImageTime += performance.now() - startTime;
|
||||
|
||||
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] canvas.drawImage seems to have worked`, "color:#000; backgroud:#2f5;");
|
||||
}
|
||||
@ -625,21 +739,25 @@ class ArDetector {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// if we are in fallback mode, then frame has already been drawn to the main canvas.
|
||||
// if we are in normal mode though, the frame has yet to be drawn
|
||||
if (!this.fallbackMode) {
|
||||
startTime = performance.now();
|
||||
this.context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
|
||||
partialDrawImageTime += performance.now() - startTime;
|
||||
}
|
||||
|
||||
this.addPerformanceTimeMeasure(this.performance.drawImage, partialDrawImageTime);
|
||||
startTime = performance.now();
|
||||
const imageData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height).data;
|
||||
this.addPerformanceTimeMeasure(this.performance.getImageData, performance.now() - startTime);
|
||||
|
||||
if (! this.fastLetterboxPresenceTest(imageData, sampleCols) ) {
|
||||
// Če ne zaznamo letterboxa, kličemo reset. Lahko, da je bilo razmerje stranic popravljeno na roke. Možno je tudi,
|
||||
// da je letterbox izginil.
|
||||
// If we don't detect letterbox, we reset aspect ratio to aspect ratio of the video file. The aspect ratio could
|
||||
// have been corrected manually. It's also possible that letterbox (that was there before) disappeared.
|
||||
this.conf.resizer.updateAr({type: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
|
||||
this.conf.resizer.updateAr({type: AspectRatioType.Automatic, ratio: this.defaultAr});
|
||||
this.guardLine.reset();
|
||||
this.noLetterboxCanvasReset = true;
|
||||
|
||||
@ -649,6 +767,7 @@ class ArDetector {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Če preverjamo naprej, potem moramo postaviti to vrednost nazaj na 'false'. V nasprotnem primeru se bo
|
||||
// css resetiral enkrat na video/pageload namesto vsakič, ko so za nekaj časa obrobe odstranejene
|
||||
// if we look further we need to reset this value back to false. Otherwise we'll only get CSS reset once
|
||||
@ -681,7 +800,7 @@ class ArDetector {
|
||||
// (since the new letterbox edge isn't present in our sample due to technical
|
||||
// limitations)
|
||||
if (this.fallbackMode && guardLineOut.blackbarFail) {
|
||||
this.conf.resizer.setAr({type: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
|
||||
this.conf.resizer.setAr({type: AspectRatioType.Automatic, ratio: this.defaultAr});
|
||||
this.guardLine.reset();
|
||||
this.noLetterboxCanvasReset = true;
|
||||
|
||||
@ -706,7 +825,7 @@ class ArDetector {
|
||||
|
||||
if(guardLineOut.blackbarFail){
|
||||
this.logger.log('info', 'arDetect', `[ArDetect::frameCheck] Detected blackbar violation and pillarbox. Resetting to default aspect ratio.`);
|
||||
this.conf.resizer.setAr({type: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
|
||||
this.conf.resizer.setAr({type: AspectRatioType.Automatic, ratio: this.defaultAr});
|
||||
this.guardLine.reset();
|
||||
}
|
||||
|
||||
@ -776,7 +895,7 @@ class ArDetector {
|
||||
// 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: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
|
||||
// this.conf.resizer.setAr({type: AspectRatioType.Automatic, ratio: this.defaultAr});
|
||||
}
|
||||
|
||||
this.clearImageData(imageData);
|
||||
@ -919,6 +1038,13 @@ class ArDetector {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a quick test to see if the aspect ratio is correct
|
||||
* Returns 'true' if there's a chance of letterbox existing, false if not.
|
||||
* @param imageData
|
||||
* @param sampleCols
|
||||
* @returns
|
||||
*/
|
||||
fastLetterboxPresenceTest(imageData, sampleCols) {
|
||||
// fast test to see if aspect ratio is correct.
|
||||
// returns 'true' if presence of letterbox is possible.
|
||||
@ -958,8 +1084,11 @@ class ArDetector {
|
||||
);
|
||||
}
|
||||
|
||||
if (currentMin < this.blackLevel)
|
||||
|
||||
|
||||
if (currentMin < this.blackLevel) {
|
||||
this.blackLevel = currentMin
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ class VideoData {
|
||||
userCssClassName: string;
|
||||
validationId: number;
|
||||
dimensions: any;
|
||||
hasDrm: boolean;
|
||||
//#endregion
|
||||
|
||||
//#region helper objects
|
||||
@ -324,7 +325,7 @@ class VideoData {
|
||||
this.pause();
|
||||
this.destroyed = true;
|
||||
try {
|
||||
this.arDetector.stop();
|
||||
this.arDetector.halt();
|
||||
this.arDetector.destroy();
|
||||
} catch (e) {}
|
||||
this.arDetector = undefined;
|
||||
@ -438,6 +439,9 @@ class VideoData {
|
||||
* instead you should be calling processDimensionChanged() wrapper function.
|
||||
*/
|
||||
private _processDimensionsChanged() {
|
||||
if (!this.player) {
|
||||
this.logger.log('warn', 'debug', `[VideoData::_processDimensionsChanged] Player is not defined. This is super haram.`, this.player)
|
||||
}
|
||||
// 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
|
||||
// restoreAr here, in case video size was changed this way
|
||||
@ -585,6 +589,9 @@ class VideoData {
|
||||
|
||||
if (hasDrm(this.video)) {
|
||||
this.player.showNotification('AARD_DRM');
|
||||
this.hasDrm = true;
|
||||
} else {
|
||||
this.hasDrm = false;
|
||||
}
|
||||
|
||||
if (!this.arDetector) {
|
||||
@ -603,14 +610,14 @@ class VideoData {
|
||||
|
||||
stopArDetection() {
|
||||
if (this.arDetector) {
|
||||
this.arDetector.stop();
|
||||
this.arDetector.halt();
|
||||
}
|
||||
}
|
||||
|
||||
pause(){
|
||||
this.paused = true;
|
||||
if(this.arDetector){
|
||||
this.arDetector.stop();
|
||||
this.arDetector.halt();
|
||||
}
|
||||
if(this.player){
|
||||
this.player.stop();
|
||||
|
@ -218,7 +218,7 @@ class Resizer {
|
||||
this.logger.log('info', 'resizer', `[Resizer::setAr] <${this.resizerId}> Something wrong with ar or the player. Doing nothing.`);
|
||||
return;
|
||||
}
|
||||
this.lastAr = {type: ar.type, ratio: ar.ratio}
|
||||
this.lastAr = {type: ar.type, ratio: ar.ratio};
|
||||
}
|
||||
|
||||
// if (this.extensionMode === ExtensionMode.Basic && !PlayerData.isFullScreen() && ar.type !== AspectRatioType.Reset) {
|
||||
@ -293,7 +293,6 @@ class Resizer {
|
||||
}
|
||||
|
||||
this.zoom.applyZoom(stretchFactors);
|
||||
|
||||
this.stretcher.chromeBugMitigation(stretchFactors);
|
||||
|
||||
let translate = this.computeOffsets(stretchFactors);
|
||||
|
@ -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": "5.0.5",
|
||||
"version": "5.1.0",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "{cf02b1a7-a01a-4e37-a609-516a283f1ed3}"
|
||||
|
@ -2,23 +2,21 @@
|
||||
<div>
|
||||
<h2>What's new</h2>
|
||||
<p>Full changelog for older versions <a href="https://github.com/tamius-han/ultrawidify/blob/master/CHANGELOG.md">is available here</a>.</p>
|
||||
<p class="label">5.0.5</p>
|
||||
<p class="label">5.1.0</p>
|
||||
<ul>
|
||||
<li>
|
||||
In 'Advanced Settings' tab of the popup: Player Detection Settings are now a bit less of a mess.
|
||||
Under the hood changes: aspect ratio autodetection now uses requestAnimationFrame instead of a setTimeout/setInterval-based loop.
|
||||
</li>
|
||||
<li>
|
||||
Fixed the background issue with app.plex.tv (<a href="https://github.com/tamius-han/ultrawidify/issues/158" target="_blank">#158</a>).<br/>
|
||||
<small><b>NOTE:</b> if you're using self-hosted plex, you will have to configure the extension for flex yourself. Refer to the <a href="https://github.com/tamius-han/ultrawidify/issues/158" target="_blank">github issue</a> for details.</small>
|
||||
Logger is sorta fixed.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<small><b>NOTE from older versions:</b> zoom limitations were introduced as a workaround for a bug caused by Chrome's/Edge's faulty hardware acceleration. Yes I know this message has been here since march, but nothing has changed.</small>
|
||||
</p>
|
||||
<p>
|
||||
<small>If you experience issues with videos being stretched incorrectly at certain zoom levels, go to:</small><br/>
|
||||
<small><code>extension popup > Advanced Settings > Browser quirk mitigations > limit zoom.</code></small>
|
||||
Hopefully that didn't break anything too much.
|
||||
</p>
|
||||
<p><small>
|
||||
<b>Known issues:</b> zooming is limited in Chromium-based browsers. This is a browser bug that no extension can fix. See <a href="https://github.com/tamius-han/ultrawidify/discussions/161" target="_blank">this</a> for more info.
|
||||
</small></p>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
Loading…
Reference in New Issue
Block a user