Merge branch 'master' into feature/player-ui
This commit is contained in:
commit
038191d38e
6
.babelrc
6
.babelrc
@ -6,9 +6,9 @@
|
|||||||
["@babel/preset-env", {
|
["@babel/preset-env", {
|
||||||
"useBuiltIns": false,
|
"useBuiltIns": false,
|
||||||
"targets": {
|
"targets": {
|
||||||
"esmodules": true,
|
"esmodules": true
|
||||||
},
|
}
|
||||||
}],
|
}]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
// {
|
// {
|
||||||
|
12
CHANGELOG.md
12
CHANGELOG.md
@ -8,14 +8,24 @@
|
|||||||
* Settings page looks ugly af right now. Maybe fix it some time later
|
* Settings page looks ugly af right now. Maybe fix it some time later
|
||||||
* other bug fixes
|
* other bug fixes
|
||||||
|
|
||||||
## v5.0 (planned major)
|
## v6.0 (planned major)
|
||||||
|
|
||||||
* WebGL autodetection
|
* WebGL autodetection
|
||||||
* in-player GUI
|
* in-player GUI
|
||||||
* Fix UI logger
|
* Fix UI logger
|
||||||
|
|
||||||
|
## v5.x (next major)
|
||||||
|
|
||||||
|
* Migrate main scripts to typescript (vue is currently not included)
|
||||||
|
|
||||||
## v4.x (current major)
|
## v4.x (current major)
|
||||||
|
|
||||||
|
### v4.5.3
|
||||||
|
|
||||||
|
* Provides workaround for the fullscreen stretching bug Chrome 88 (or a recent Windows 10 update) introduced for nVidia users using hardware acceleration on Windows 10. In order to mitigate this bug, Ultrawidify needs to keep a 5-10 px wide black border while watching videos in full screen. This bug is also present in Edge.
|
||||||
|
* **[4.5.3.1]** Fixed letterbox misalignment binding in settings (#134)
|
||||||
|
* **[4.5.3.2]** Fixed false 'autodetection not supported' notifications.
|
||||||
|
|
||||||
### v4.5.2
|
### v4.5.2
|
||||||
|
|
||||||
* Fixed the issue where videos would sometimes get misaligned while using hybrid stretch, except for real this time. ([#125](https://github.com/tamius-han/ultrawidify/issues/125))
|
* Fixed the issue where videos would sometimes get misaligned while using hybrid stretch, except for real this time. ([#125](https://github.com/tamius-han/ultrawidify/issues/125))
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Ultrawidify — aspect ratio fixer for youtube and netflix
|
# Ultrawidify — aspect ratio fixer for youtube and netflix
|
||||||
|
|
||||||
## Super TL;DR: I'm just looking for the install links, thanks
|
## Super TL;DR: I'm just looking for the install links, thanks
|
||||||
|
|
||||||
|
3230
package-lock.json
generated
3230
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ultrawidify",
|
"name": "ultrawidify",
|
||||||
"version": "4.5.2",
|
"version": "5.0.0",
|
||||||
"description": "Aspect ratio fixer for youtube and other sites, with automatic aspect ratio detection. Supports ultrawide and other ratios.",
|
"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>",
|
"author": "Tamius Han <tamius.han@gmail.com>",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -21,8 +21,11 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-proposal-class-properties": "^7.12.1",
|
"@babel/plugin-proposal-class-properties": "^7.12.1",
|
||||||
|
"@types/chrome": "0.0.129",
|
||||||
"@types/core-js": "^2.5.3",
|
"@types/core-js": "^2.5.3",
|
||||||
"@types/es6-promise": "^3.3.0",
|
"@types/es6-promise": "^3.3.0",
|
||||||
|
"@types/firefox": "0.0.30",
|
||||||
|
"@types/node": "^14.14.25",
|
||||||
"@vue/cli": "^4.5.9",
|
"@vue/cli": "^4.5.9",
|
||||||
"bootstrap": "^4.5.3",
|
"bootstrap": "^4.5.3",
|
||||||
"bootstrap-icons": "^1.1.0",
|
"bootstrap-icons": "^1.1.0",
|
||||||
@ -31,17 +34,21 @@
|
|||||||
"concurrently": "^5.2.0",
|
"concurrently": "^5.2.0",
|
||||||
"fs-extra": "^7.0.1",
|
"fs-extra": "^7.0.1",
|
||||||
"json-cyclic": "0.0.3",
|
"json-cyclic": "0.0.3",
|
||||||
|
"lodash": "^4.17.20",
|
||||||
"vue": "^3.0.0-beta.1",
|
"vue": "^3.0.0-beta.1",
|
||||||
"vuex": "^4.0.0-alpha.1",
|
"vuex": "^4.0.0-alpha.1",
|
||||||
"vuex-webextensions": "^1.3.0"
|
"vuex-webextensions": "^1.3.0",
|
||||||
|
"webextension-polyfill-ts": "^0.24.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.10.5",
|
"@babel/core": "^7.12.13",
|
||||||
"@babel/plugin-proposal-optional-chaining": "^7.10.4",
|
"@babel/plugin-proposal-optional-chaining": "^7.10.4",
|
||||||
"@babel/preset-env": "^7.10.4",
|
"@babel/preset-env": "^7.12.13",
|
||||||
|
"@types/lodash": "^4.14.168",
|
||||||
"@vue/compiler-sfc": "^3.0.3",
|
"@vue/compiler-sfc": "^3.0.3",
|
||||||
"archiver": "^3.0.0",
|
"archiver": "^3.0.0",
|
||||||
"babel-loader": "^8.1.0",
|
"babel-loader": "^8.2.2",
|
||||||
|
"babel-preset-es2020": "^1.0.2",
|
||||||
"copy-webpack-plugin": "^4.5.3",
|
"copy-webpack-plugin": "^4.5.3",
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^5.2.0",
|
||||||
"css-loader": "^0.28.11",
|
"css-loader": "^0.28.11",
|
||||||
@ -50,9 +57,10 @@
|
|||||||
"mini-css-extract-plugin": "^0.4.4",
|
"mini-css-extract-plugin": "^0.4.4",
|
||||||
"node-sass": "^4.14.1",
|
"node-sass": "^4.14.1",
|
||||||
"sass-loader": "^7.1.0",
|
"sass-loader": "^7.1.0",
|
||||||
|
"ts-loader": "^8.0.16",
|
||||||
"vue-cli-plugin-vue-next": "~0.1.4",
|
"vue-cli-plugin-vue-next": "~0.1.4",
|
||||||
"vue-loader": "^16.0.0",
|
"vue-loader": "^16.0.0",
|
||||||
"web-ext-types": "^2.1.0",
|
"web-ext-types": "^2.3.0",
|
||||||
"webextension-polyfill": "^0.6.0",
|
"webextension-polyfill": "^0.6.0",
|
||||||
"webpack": "^4.44.0",
|
"webpack": "^4.44.0",
|
||||||
"webpack-chrome-extension-reloader": "^0.8.3",
|
"webpack-chrome-extension-reloader": "^0.8.3",
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex action-name">
|
<div class="flex action-name">
|
||||||
<span v-if="action.cmd && action.cmd.length > 1 || action.cmd[0].action === 'set-ar' && action.userAdded || (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.userAdded || (action.cmd[0].arg === AspectRatioType.Fixed)" class="icon red" @click="removeAction()">🗙</span>
|
||||||
<span v-else class="icon transparent">🗙</span>
|
<span v-else class="icon transparent">🗙</span>
|
||||||
<span class="icon" @click="editAction()">🖉</span>
|
<span class="icon" @click="editAction()">🖉</span>
|
||||||
{{action.name}}
|
{{action.name}}
|
||||||
@ -108,8 +108,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Stretch from '../enums/stretch.enum';
|
import StretchType from '../enums/StretchType.enum';
|
||||||
import AspectRatio from '../enums/aspect-ratio.enum';
|
import AspectRatioType from '../enums/AspectRatioType.enum';
|
||||||
import KeyboardShortcutParser from '../js/KeyboardShortcutParser';
|
import KeyboardShortcutParser from '../js/KeyboardShortcutParser';
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Stretch from '../enums/stretch.enum';
|
import StretchType from '../enums/StretchType.enum';
|
||||||
import KeyboardShortcutParser from '../js/KeyboardShortcutParser'
|
import KeyboardShortcutParser from '../js/KeyboardShortcutParser'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -6,7 +6,7 @@ let Notifications = Object.freeze({
|
|||||||
},
|
},
|
||||||
'AARD_DRM': {
|
'AARD_DRM': {
|
||||||
icon: 'exclamation-triangle',
|
icon: 'exclamation-triangle',
|
||||||
text: '<b>Autodetection cannot run on this video.</b> This usually happens when sites use DRM. You will have to set aspect ratio manually.',
|
text: '<b>Autodetection may not be able to run on this video.</b> On sites that utilize DRM you will have to set aspect ratio manually.',
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
7
src/common/enums/AntiGradientMode.enum.ts
Normal file
7
src/common/enums/AntiGradientMode.enum.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
enum AntiGradientMode {
|
||||||
|
Disabled = 0,
|
||||||
|
Lax = 1,
|
||||||
|
Strict = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AntiGradientMode;
|
11
src/common/enums/AspectRatioType.enum.ts
Normal file
11
src/common/enums/AspectRatioType.enum.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
enum AspectRatioType {
|
||||||
|
Initial = -1, // page default
|
||||||
|
Reset = 0, // reset to initial
|
||||||
|
Automatic = 1, // set by Aard
|
||||||
|
FitWidth = 2, // legacy/dynamic = fit to width
|
||||||
|
FitHeight = 3, // legacy/dynamic = fit to height
|
||||||
|
Fixed = 4, // pre-determined aspect ratio
|
||||||
|
Manual = 5, // ratio achieved by zooming in/zooming out
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AspectRatioType;
|
9
src/common/enums/CropModePersistence.enum.ts
Normal file
9
src/common/enums/CropModePersistence.enum.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
enum CropModePersistence {
|
||||||
|
Default = -1,
|
||||||
|
Disabled = 0,
|
||||||
|
UntilPageReload = 1,
|
||||||
|
CurrentSession = 2,
|
||||||
|
Forever = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CropModePersistence;
|
10
src/common/enums/ExtensionMode.enum.ts
Normal file
10
src/common/enums/ExtensionMode.enum.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
enum ExtensionMode {
|
||||||
|
AutoDisabled = -2,
|
||||||
|
Disabled = -1,
|
||||||
|
Default = 0,
|
||||||
|
Whitelist = 1,
|
||||||
|
Basic = 2,
|
||||||
|
Enabled = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ExtensionMode;
|
11
src/common/enums/StretchType.enum.ts
Normal file
11
src/common/enums/StretchType.enum.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
enum StretchType {
|
||||||
|
NoStretch = 0,
|
||||||
|
Basic = 1,
|
||||||
|
Hybrid = 2,
|
||||||
|
Conditional = 3,
|
||||||
|
Fixed = 4,
|
||||||
|
FixedSource = 5,
|
||||||
|
Default = -1
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StretchType;
|
8
src/common/enums/VideoAlignmentType.enum.ts
Normal file
8
src/common/enums/VideoAlignmentType.enum.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
enum VideoAlignmentType {
|
||||||
|
Left = 0,
|
||||||
|
Center = 1,
|
||||||
|
Right = 2,
|
||||||
|
Default = -1
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VideoAlignmentType;
|
@ -1,7 +0,0 @@
|
|||||||
var AntiGradientMode = Object.freeze({
|
|
||||||
Disabled: 0,
|
|
||||||
Lax: 1,
|
|
||||||
Strict: 2
|
|
||||||
});
|
|
||||||
|
|
||||||
export default AntiGradientMode;
|
|
@ -1,24 +0,0 @@
|
|||||||
var AspectRatio = Object.freeze({
|
|
||||||
Initial: -1, // page default
|
|
||||||
Reset: 0, // reset to initial
|
|
||||||
Automatic: 1, // set by Aard
|
|
||||||
FitWidth: 2, // legacy/dynamic: fit to width
|
|
||||||
FitHeight: 3, // legacy/dynamic: fit to height
|
|
||||||
Fixed: 4, // pre-determined aspect ratio
|
|
||||||
Manual: 5, // ratio achieved by zooming in/zooming out
|
|
||||||
|
|
||||||
toString: (ar) => {
|
|
||||||
switch (ar) {
|
|
||||||
case -1: return 'Initial';
|
|
||||||
case 0: return 'Reset';
|
|
||||||
case 1: return 'Automatic';
|
|
||||||
case 2: return 'FitWidth';
|
|
||||||
case 3: return 'FitHeight';
|
|
||||||
case 4: return 'Fixed';
|
|
||||||
case 5: return 'Manual';
|
|
||||||
default: return '<not an valid enum value>'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default AspectRatio;
|
|
@ -1,26 +0,0 @@
|
|||||||
var CropModePersistence = Object.freeze({
|
|
||||||
Default: -1,
|
|
||||||
Disabled: 0,
|
|
||||||
UntilPageReload: 1,
|
|
||||||
CurrentSession: 2,
|
|
||||||
Forever: 3,
|
|
||||||
|
|
||||||
toString: (cropModePersistence) => {
|
|
||||||
switch (cropModePersistence) {
|
|
||||||
case -1:
|
|
||||||
return 'default';
|
|
||||||
case 0:
|
|
||||||
return 'disabled';
|
|
||||||
case 1:
|
|
||||||
return 'until page reload';
|
|
||||||
case 2:
|
|
||||||
return 'current session';
|
|
||||||
case 3:
|
|
||||||
return 'forever';
|
|
||||||
default:
|
|
||||||
return 'unknown mode';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default CropModePersistence;
|
|
@ -1,19 +0,0 @@
|
|||||||
if (process.env.CHANNEL !== 'stable') {
|
|
||||||
console.info('Loading ExtensionMode');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var ExtensionMode = Object.freeze({
|
|
||||||
AutoDisabled: -2,
|
|
||||||
Disabled: -1,
|
|
||||||
Default: 0,
|
|
||||||
Whitelist: 1,
|
|
||||||
Basic: 2,
|
|
||||||
Enabled: 3,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (process.env.CHANNEL !== 'stable') {
|
|
||||||
console.info('Loaded ExtensionMode');
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExtensionMode;
|
|
@ -1,31 +0,0 @@
|
|||||||
var Stretch = Object.freeze({
|
|
||||||
NoStretch: 0,
|
|
||||||
Basic: 1,
|
|
||||||
Hybrid: 2,
|
|
||||||
Conditional: 3,
|
|
||||||
Fixed: 4,
|
|
||||||
FixedSource: 5,
|
|
||||||
Default: -1,
|
|
||||||
toString: (stretch) => {
|
|
||||||
switch (stretch) {
|
|
||||||
case 0:
|
|
||||||
return 'NoStretch';
|
|
||||||
case 1:
|
|
||||||
return 'Basic';
|
|
||||||
case 2:
|
|
||||||
return 'Hybrid';
|
|
||||||
case 3:
|
|
||||||
return 'Conditional';
|
|
||||||
case 4:
|
|
||||||
return 'Fixed';
|
|
||||||
case 5:
|
|
||||||
return 'FixedSource';
|
|
||||||
case -1:
|
|
||||||
return 'Default';
|
|
||||||
default:
|
|
||||||
return 'INVALID STRETCH VALUE';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Stretch;
|
|
@ -1,22 +0,0 @@
|
|||||||
var VideoAlignment = Object.freeze({
|
|
||||||
Left: 0,
|
|
||||||
Center: 1,
|
|
||||||
Right: 2,
|
|
||||||
Default: -1,
|
|
||||||
toString: (alignment) => {
|
|
||||||
switch (alignment) {
|
|
||||||
case 0:
|
|
||||||
return 'Left';
|
|
||||||
case 1:
|
|
||||||
return 'Center';
|
|
||||||
case 2:
|
|
||||||
return 'Right';
|
|
||||||
case -1:
|
|
||||||
return 'Default';
|
|
||||||
default:
|
|
||||||
return 'ILLEGAL VIDEO ALIGNMENT';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default VideoAlignment;
|
|
280
src/common/interfaces/SettingsInterface.ts
Normal file
280
src/common/interfaces/SettingsInterface.ts
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
import { Action } from '../../../node_modules/vuex/types/index'
|
||||||
|
import AntiGradientMode from '../enums/AntiGradientMode.enum'
|
||||||
|
import AspectRatioType from '../enums/AspectRatioType.enum'
|
||||||
|
import CropModePersistence from '../enums/CropModePersistence.enum'
|
||||||
|
import ExtensionMode from '../enums/ExtensionMode.enum'
|
||||||
|
import StretchType from '../enums/StretchType.enum'
|
||||||
|
import VideoAlignmentType from '../enums/VideoAlignmentType.enum'
|
||||||
|
|
||||||
|
interface ActionScopeInterface {
|
||||||
|
show: boolean,
|
||||||
|
label?: string, // example override, takes precedence over default label
|
||||||
|
shortcut?: {
|
||||||
|
key?: string,
|
||||||
|
code?: string,
|
||||||
|
ctrlKey?: boolean,
|
||||||
|
metaKey?: boolean,
|
||||||
|
altKey?: boolean,
|
||||||
|
shiftKey?: boolean,
|
||||||
|
onKeyUp?: boolean,
|
||||||
|
onKeyDown?: boolean,
|
||||||
|
onMouseMove?: boolean,
|
||||||
|
}[],
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SettingsInterface {
|
||||||
|
arDetect: {
|
||||||
|
disabledReason: string, // if automatic aspect ratio has been disabled, show reason
|
||||||
|
allowedMisaligned: number, // top and bottom letterbox thickness can differ by this much.
|
||||||
|
// Any more and we don't adjust ar.
|
||||||
|
allowedArVariance: number, // amount by which old ar can differ from the new (1 = 100%)
|
||||||
|
timers: { // autodetection frequency
|
||||||
|
playing: number, // while playing
|
||||||
|
paused: number, // while paused
|
||||||
|
error: number, // after error
|
||||||
|
minimumTimeout: number,
|
||||||
|
tickrate: number, // 1 tick every this many milliseconds
|
||||||
|
},
|
||||||
|
autoDisable: { // settings for automatically disabling the extension
|
||||||
|
maxExecutionTime: number, // if execution time of main autodetect loop exceeds this many milliseconds,
|
||||||
|
// we disable it.
|
||||||
|
consecutiveTimeoutCount: number, // we only do it if it happens this many consecutive times
|
||||||
|
|
||||||
|
// FOR FUTURE USE
|
||||||
|
consecutiveArResets: number // if aspect ratio reverts immediately after AR change is applied, we disable everything
|
||||||
|
},
|
||||||
|
canvasDimensions: {
|
||||||
|
blackframeCanvas: { // smaller than sample canvas, blackframe canvas is used to recon for black frames
|
||||||
|
// it's not used to detect aspect ratio by itself, so it can be tiny af
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
},
|
||||||
|
sampleCanvas: { // size of image sample for detecting aspect ratio. Bigger size means more accurate results,
|
||||||
|
// at the expense of performance
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// samplingInterval: 10, // we sample at columns at (width/this) * [ 1 .. this - 1]
|
||||||
|
blackframe: {
|
||||||
|
sufficientColorVariance: number, // calculate difference between average intensity and pixel, for every pixel for every color
|
||||||
|
// component. Average intensity is normalized to where 0 is black and 1 is biggest value for
|
||||||
|
// that component. If sum of differences between normalized average intensity and normalized
|
||||||
|
// component varies more than this % between color components, we can afford to use less strict
|
||||||
|
// cumulative threshold.
|
||||||
|
cumulativeThresholdLax: number,
|
||||||
|
cumulativeThresholdStrict: number,// if we add values of all pixels together and get more than this, the frame is bright enough.
|
||||||
|
// (note: blackframe is 16x9 px -> 144px total. cumulative threshold can be reached fast)
|
||||||
|
blackPixelsCondition: number, // How much pixels must be black (1 all, 0 none) before we consider frame as black. Takes
|
||||||
|
// precedence over cumulative threshold: if blackPixelsCondition is met, the frame is dark
|
||||||
|
// regardless of whether cumulative threshold has been reached.
|
||||||
|
},
|
||||||
|
blackbar: {
|
||||||
|
blackLevel: number, // everything darker than 10/255 across all RGB components is considered black by
|
||||||
|
// default. blackLevel can decrease if we detect darker black.
|
||||||
|
threshold: number, // if pixel is darker than the sum of black level and this value, we count it as black
|
||||||
|
// on 0-255. Needs to be fairly high (8 might not cut it) due to compression
|
||||||
|
// artifacts in the video itself
|
||||||
|
frameThreshold: number, // threshold, but when doing blackframe test
|
||||||
|
imageThreshold: number, // in order to detect pixel as "not black", the pixel must be brighter than
|
||||||
|
// the sum of black level, threshold and this value.
|
||||||
|
gradientThreshold: number, // When trying to determine thickness of the black bars, we take 2 values: position of
|
||||||
|
// the last pixel that's darker than our threshold, and position of the first pixel that's
|
||||||
|
// brighter than our image threshold. If positions are more than this many pixels apart,
|
||||||
|
// we assume we aren't looking at letterbox and thus don't correct the aspect ratio.
|
||||||
|
gradientSampleSize: number, // How far do we look to find the gradient
|
||||||
|
maxGradient: number, // if two neighboring pixels in gradientSampleSize differ by more than this, then we aren't
|
||||||
|
// looking at a gradient
|
||||||
|
gradientNegativeTreshold: number,
|
||||||
|
gradientMaxSD: number, // reserved for future use
|
||||||
|
antiGradientMode: AntiGradientMode
|
||||||
|
},
|
||||||
|
variableBlackbarThresholdOptions: { // In case of poor bitrate videos, jpeg artifacts may cause us issues
|
||||||
|
// FOR FUTURE USE
|
||||||
|
enabled: boolean, // allow increasing blackbar threshold
|
||||||
|
disableArDetectOnMax: boolean, // disable autodetection when threshold goes over max blackbar threshold
|
||||||
|
maxBlackbarThreshold: number, // max threshold (don't increase past this)
|
||||||
|
thresholdStep: number, // when failing to set aspect ratio, increase threshold by this much
|
||||||
|
increaseAfterConsecutiveResets: number // increase if AR resets this many times in a row
|
||||||
|
},
|
||||||
|
sampling: {
|
||||||
|
staticCols: number, // we take a column at [0-n]/n-th parts along the width and sample it
|
||||||
|
randomCols: number, // we add this many randomly selected columns to the static columns
|
||||||
|
staticRows: number, // forms grid with staticSampleCols. Determined in the same way. For black frame checks
|
||||||
|
},
|
||||||
|
guardLine: { // all pixels on the guardline need to be black, or else we trigger AR recalculation
|
||||||
|
// (if AR fails to be recalculated, we reset AR)
|
||||||
|
enabled: boolean,
|
||||||
|
ignoreEdgeMargin: number, // we ignore anything that pokes over the black line this close to the edge
|
||||||
|
// (relative to width of the sample)
|
||||||
|
imageTestThreshold: number, // when testing for image, this much pixels must be over blackbarThreshold
|
||||||
|
edgeTolerancePx: number, // black edge violation is performed this far from reported 'last black pixel'
|
||||||
|
edgeTolerancePercent: null // unused. same as above, except use % of canvas height instead of pixels
|
||||||
|
},
|
||||||
|
fallbackMode: {
|
||||||
|
enabled: boolean,
|
||||||
|
safetyBorderPx: number, // determines the thickness of safety border in fallback mode
|
||||||
|
noTriggerZonePx: number // if we detect edge less than this many pixels thick, we don't correct.
|
||||||
|
},
|
||||||
|
arSwitchLimiter: { // to be implemented
|
||||||
|
switches: number, // we can switch this many times
|
||||||
|
period: number // per this period
|
||||||
|
},
|
||||||
|
edgeDetection: {
|
||||||
|
sampleWidth: number, // we take a sample this wide for edge detection
|
||||||
|
detectionThreshold: number, // sample needs to have this many non-black pixels to be a valid edge
|
||||||
|
confirmationThreshold: number, //
|
||||||
|
singleSideConfirmationThreshold: number, // we need this much edges (out of all samples, not just edges) in order
|
||||||
|
// to confirm an edge in case there's no edges on top or bottom (other
|
||||||
|
// than logo, of course)
|
||||||
|
logoThreshold: number, // if edge candidate sits with count greater than this*all_samples, it can't be logo
|
||||||
|
// or watermark.
|
||||||
|
edgeTolerancePx?: number, // we check for black edge violation this far from detection point
|
||||||
|
edgeTolerancePercent?: number, // we check for black edge detection this % of height from detection point. unused
|
||||||
|
middleIgnoredArea: number, // we ignore this % of canvas height towards edges while detecting aspect ratios
|
||||||
|
minColsForSearch: number, // if we hit the edge of blackbars for all but this many columns (%-wise), we don't
|
||||||
|
// continue with search. It's pointless, because black edge is higher/lower than we
|
||||||
|
// are now. (NOTE: keep this less than 1 in case we implement logo detection)
|
||||||
|
},
|
||||||
|
pillarTest: {
|
||||||
|
ignoreThinPillarsPx: number, // ignore pillars that are less than this many pixels thick.
|
||||||
|
allowMisaligned: number // left and right edge can vary this much (%)
|
||||||
|
},
|
||||||
|
textLineTest: {
|
||||||
|
nonTextPulse: number, // if a single continuous pulse has this many non-black pixels, we aren't dealing
|
||||||
|
// with text. This value is relative to canvas width (%)
|
||||||
|
pulsesToConfirm: number, // this is a threshold to confirm we're seeing text.
|
||||||
|
pulsesToConfirmIfHalfBlack: number, // this is the threshold to confirm we're seeing text if longest black pulse
|
||||||
|
// is over 50% of the canvas width
|
||||||
|
testRowOffset: number // we test this % of height from detected edge
|
||||||
|
}
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
minLogZoom: number,
|
||||||
|
maxLogZoom: number,
|
||||||
|
announceDebounce: number // we wait this long before announcing new zoom
|
||||||
|
},
|
||||||
|
miscSettings: {
|
||||||
|
mousePan: {
|
||||||
|
enabled: boolean
|
||||||
|
},
|
||||||
|
mousePanReverseMouse: boolean,
|
||||||
|
defaultAr?: any
|
||||||
|
},
|
||||||
|
stretch: {
|
||||||
|
conditionalDifferencePercent: number // black bars less than this wide will trigger stretch
|
||||||
|
// if mode is set to '1'. 1.0=100%
|
||||||
|
},
|
||||||
|
resizer: {
|
||||||
|
setStyleString: {
|
||||||
|
maxRetries: number,
|
||||||
|
retryTimeout: number
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pageInfo: {
|
||||||
|
timeouts: {
|
||||||
|
urlCheck: number,
|
||||||
|
rescan: number
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pan?: any,
|
||||||
|
version?: string,
|
||||||
|
preventReload?: boolean,
|
||||||
|
// -----------------------------------------
|
||||||
|
// ::: ACTIONS :::
|
||||||
|
// -----------------------------------------
|
||||||
|
// Nastavitve za ukaze. Zamenja stare nastavitve za bližnične tipke.
|
||||||
|
//
|
||||||
|
// Polje 'shortcut' je tabela, če se slučajno lotimo kdaj delati choordov.
|
||||||
|
actions: {
|
||||||
|
name?: string, // name displayed in settings
|
||||||
|
label?: string, // name displayed in ui (can be overridden in scope/playerUi)
|
||||||
|
cmd?: {
|
||||||
|
action: string,
|
||||||
|
arg: any,
|
||||||
|
customArg?: any,
|
||||||
|
persistent?: boolean, // optional, false by default. If true, change doesn't take effect immediately.
|
||||||
|
// Instead, this action saves stuff to settings
|
||||||
|
}[],
|
||||||
|
scopes?: {
|
||||||
|
global?: ActionScopeInterface,
|
||||||
|
site?: ActionScopeInterface,
|
||||||
|
page?: ActionScopeInterface
|
||||||
|
},
|
||||||
|
playerUi?: {
|
||||||
|
show: boolean,
|
||||||
|
path?: string,
|
||||||
|
},
|
||||||
|
userAdded?: boolean,
|
||||||
|
}[],
|
||||||
|
whatsNewChecked: boolean,
|
||||||
|
// -----------------------------------------
|
||||||
|
// ::: SITE CONFIGURATION :::
|
||||||
|
// -----------------------------------------
|
||||||
|
// Nastavitve za posamezno stran
|
||||||
|
// Config for a given page:
|
||||||
|
//
|
||||||
|
// <hostname> : {
|
||||||
|
// status: <option> // should extension work on this site?
|
||||||
|
// arStatus: <option> // should we do autodetection on this site?
|
||||||
|
//
|
||||||
|
// defaultAr?: <ratio> // automatically apply this aspect ratio on this side. Use extension defaults if undefined.
|
||||||
|
// stretch? <stretch mode> // automatically stretch video on this site in this manner
|
||||||
|
// videoAlignment? <left|center|right>
|
||||||
|
//
|
||||||
|
// type: <official|community|user> // 'official' — blessed by Tam.
|
||||||
|
// // 'community' — blessed by reddit.
|
||||||
|
// // 'user' — user-defined (not here)
|
||||||
|
// override: <true|false> // override user settings for this site on update
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Veljavne vrednosti za možnosti
|
||||||
|
// Valid values for options:
|
||||||
|
//
|
||||||
|
// status, arStatus, statusEmbedded:
|
||||||
|
//
|
||||||
|
// * enabled — always allow, full
|
||||||
|
// * basic — allow, but only the basic version without playerData
|
||||||
|
// * default — allow if default is to allow, block if default is to block
|
||||||
|
// * disabled — never allow
|
||||||
|
//
|
||||||
|
sites: {
|
||||||
|
[x: string]: {
|
||||||
|
mode?: ExtensionMode,
|
||||||
|
autoar?: ExtensionMode,
|
||||||
|
autoarFallback?: ExtensionMode,
|
||||||
|
stretch?: StretchType,
|
||||||
|
videoAlignment?: VideoAlignmentType,
|
||||||
|
keyboardShortcutsEnabled?: ExtensionMode,
|
||||||
|
type?: string,
|
||||||
|
override?: boolean,
|
||||||
|
arPersistence?: boolean,
|
||||||
|
actions?: any;
|
||||||
|
|
||||||
|
cropModePersistence?: CropModePersistence;
|
||||||
|
|
||||||
|
DOM?: {
|
||||||
|
player?: {
|
||||||
|
manual?: boolean,
|
||||||
|
querySelectors?: string,
|
||||||
|
additionalCss?: string,
|
||||||
|
useRelativeAncestor?: boolean,
|
||||||
|
videoAncestor?: any,
|
||||||
|
playerNodeCss?: string,
|
||||||
|
periodicallyRefreshPlayerElement?: boolean
|
||||||
|
},
|
||||||
|
video?: {
|
||||||
|
manual?: boolean,
|
||||||
|
querySelectors?: string,
|
||||||
|
additionalCss?: string,
|
||||||
|
useRelativeAncestor?: boolean,
|
||||||
|
playerNodeCss?: string
|
||||||
|
}
|
||||||
|
},
|
||||||
|
css?: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SettingsInterface;
|
@ -10,7 +10,7 @@ export default {
|
|||||||
}) || [];
|
}) || [];
|
||||||
},
|
},
|
||||||
extensionActions: function(){
|
extensionActions: function(){
|
||||||
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-extension-mode') || [];
|
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-ExtensionMode') || [];
|
||||||
},
|
},
|
||||||
aardActions: function(){
|
aardActions: function(){
|
||||||
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-autoar-mode') || [];
|
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-autoar-mode') || [];
|
||||||
|
134
src/ext/UWContent.ts
Normal file
134
src/ext/UWContent.ts
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
import Debug from './conf/Debug';
|
||||||
|
import ExtensionMode from '../common/enums/ExtensionMode.enum';
|
||||||
|
import Settings from './lib/Settings';
|
||||||
|
import ActionHandler from './lib/ActionHandler';
|
||||||
|
import Comms from './lib/comms/Comms';
|
||||||
|
import CommsClient from './lib/comms/CommsClient';
|
||||||
|
import PageInfo from './lib/video-data/PageInfo';
|
||||||
|
import Logger, { baseLoggingOptions } from './lib/Logger';
|
||||||
|
|
||||||
|
export default class UWContent {
|
||||||
|
pageInfo: PageInfo;
|
||||||
|
comms: CommsClient;
|
||||||
|
settings: Settings;
|
||||||
|
actionHandler: ActionHandler;
|
||||||
|
logger: Logger;
|
||||||
|
|
||||||
|
commsHandlers: {
|
||||||
|
[x: string]: ((a: any, b?: any) => void | Promise<void>)[]
|
||||||
|
} = {
|
||||||
|
'get-current-zoom': [() => this.pageInfo.requestCurrentZoom()],
|
||||||
|
'set-ar': [(message) => this.pageInfo.setAr({type: message.arg, ratio: message.customArg}, message.playing)],
|
||||||
|
'set-alignment': [(message) => {
|
||||||
|
this.pageInfo.setVideoAlignment(message.arg, message.playing);
|
||||||
|
this.pageInfo.restoreAr();
|
||||||
|
}],
|
||||||
|
'set-stretch': [(message) => this.pageInfo.setStretchMode(message.arg, message.playing, message.customArg)],
|
||||||
|
'set-keyboard': [(message) => this.pageInfo.setKeyboardShortcutsEnabled(message.arg)],
|
||||||
|
'autoar-start': [(message) => {
|
||||||
|
if (message.enabled !== false) {
|
||||||
|
this.pageInfo.initArDetection(message.playing);
|
||||||
|
this.pageInfo.startArDetection(message.playing);
|
||||||
|
} else {
|
||||||
|
this.pageInfo.stopArDetection(message.playing);
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
'pause-processing': [(message) => this.pageInfo.pauseProcessing(message.playing)],
|
||||||
|
'resume-processing': [(message) => this.pageInfo.resumeProcessing(message.autoArStatus, message.playing)],
|
||||||
|
'set-zoom': [(message) => this.pageInfo.setZoom(message.arg, true, message.playing)],
|
||||||
|
'change-zoom': [(message) => this.pageInfo.zoomStep(message.arg, message.playing)],
|
||||||
|
'mark-player': [(message) => this.pageInfo.markPlayer(message.name, message.color)],
|
||||||
|
'unmark-player': [() => this.pageInfo.unmarkPlayer()],
|
||||||
|
'autoar-set-manual-tick': [(message) => this.pageInfo.setManualTick(message.arg)],
|
||||||
|
'autoar-tick': [() => this.pageInfo.tick()],
|
||||||
|
'set-ar-persistence': [(message) => this.pageInfo.setArPersistence(message.arg)],
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadSettings() {
|
||||||
|
this.logger.log('info', 'debug', 'Things happened in the popup. Will reload extension settings.');
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
async init(){
|
||||||
|
if (Debug.debug) {
|
||||||
|
console.log("[uw::main] loading configuration ...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// logger init is the first thing that needs to run
|
||||||
|
try {
|
||||||
|
if (!this.logger) {
|
||||||
|
|
||||||
|
this.logger = new Logger();
|
||||||
|
await this.logger.init(baseLoggingOptions);
|
||||||
|
|
||||||
|
// show popup if logging to file is enabled
|
||||||
|
if (this.logger.isLoggingAllowed() && this.logger.isLoggingToFile()) {
|
||||||
|
console.info("[uw::init] Logging is allowed! Initalizing vue and UI!");
|
||||||
|
|
||||||
|
// CommsClient is not initiated yet, so we use static comms to send the command
|
||||||
|
Comms.sendMessage({cmd: 'show-logger'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("logger init failed!", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// init() is re-run any time settings change
|
||||||
|
if (this.comms) {
|
||||||
|
this.comms.destroy();
|
||||||
|
}
|
||||||
|
if (!this.settings) {
|
||||||
|
this.settings = new Settings({
|
||||||
|
onSettingsChanged: () => this.reloadSettings(),
|
||||||
|
logger: this.logger
|
||||||
|
});
|
||||||
|
await this.settings.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.comms = new CommsClient('content-main-port', this.logger, this.commsHandlers);
|
||||||
|
|
||||||
|
// če smo razširitev onemogočili v nastavitvah, ne naredimo ničesar
|
||||||
|
// If extension is soft-disabled, don't do shit
|
||||||
|
|
||||||
|
var extensionMode = this.settings.getExtensionMode();
|
||||||
|
|
||||||
|
this.logger.log('info', 'debug', "[uw::init] Extension mode:" + (extensionMode < 0 ? "disabled" : extensionMode == '1' ? 'basic' : 'full'));
|
||||||
|
|
||||||
|
const isSiteDisabled = extensionMode === ExtensionMode.Disabled
|
||||||
|
|
||||||
|
if (isSiteDisabled) {
|
||||||
|
if (this.settings.getExtensionMode('@global') === ExtensionMode.Disabled) {
|
||||||
|
this.logger.log('info', 'debug', "[uw::init] EXTENSION DISABLED, THEREFORE WONT BE STARTED")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (this.pageInfo) {
|
||||||
|
this.logger.log('info', 'debug', '[uw.js::setup] An instance of pageInfo already exists and will be destroyed.');
|
||||||
|
this.pageInfo.destroy();
|
||||||
|
}
|
||||||
|
this.pageInfo = new PageInfo(this.comms, this.settings, this.logger, extensionMode, isSiteDisabled);
|
||||||
|
this.logger.log('info', 'debug', "[uw.js::setup] pageInfo initialized.");
|
||||||
|
|
||||||
|
this.logger.log('info', 'debug', "[uw.js::setup] will try to initate ActionHandler.");
|
||||||
|
|
||||||
|
// start action handler only if extension is enabled for this site
|
||||||
|
if (!isSiteDisabled) {
|
||||||
|
if (this.actionHandler) {
|
||||||
|
this.actionHandler.destroy();
|
||||||
|
}
|
||||||
|
this.actionHandler = new ActionHandler(this.pageInfo);
|
||||||
|
this.actionHandler.init();
|
||||||
|
|
||||||
|
this.logger.log('info', 'debug', "[uw.js::setup] ActionHandler initiated.");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.log('error', 'debug', "[uw::init] FAILED TO START EXTENSION. Error:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
307
src/ext/UWServer.ts
Normal file
307
src/ext/UWServer.ts
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
import Debug from './conf/Debug.js';
|
||||||
|
import BrowserDetect from './conf/BrowserDetect';
|
||||||
|
import CommsServer from './lib/comms/CommsServer';
|
||||||
|
import Settings from './lib/Settings';
|
||||||
|
import Logger, { baseLoggingOptions } from './lib/Logger';
|
||||||
|
|
||||||
|
import { sleep } from '../common/js/utils';
|
||||||
|
|
||||||
|
import { browser } from 'webextension-polyfill-ts';
|
||||||
|
|
||||||
|
export default class UWServer {
|
||||||
|
settings: Settings;
|
||||||
|
logger: Logger;
|
||||||
|
comms: CommsServer;
|
||||||
|
|
||||||
|
ports: any[] = [];
|
||||||
|
hasVideos: boolean;
|
||||||
|
currentSite: string = '';
|
||||||
|
videoTabs: any;
|
||||||
|
currentTabId: number = 0;
|
||||||
|
|
||||||
|
selectedSubitem: any = {
|
||||||
|
'siteSettings': undefined,
|
||||||
|
'videoSettings': undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private gcTimeout: any;
|
||||||
|
uiLoggerInitialized: boolean = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
async setup() {
|
||||||
|
// logger is the first thing that goes up
|
||||||
|
|
||||||
|
const loggingOptions = {
|
||||||
|
isBackgroundScript: true,
|
||||||
|
allowLogging: true,
|
||||||
|
useConfFromStorage: true,
|
||||||
|
logAll: true,
|
||||||
|
fileOptions: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
consoleOptions: {
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.logger = new Logger();
|
||||||
|
await this.logger.init(loggingOptions);
|
||||||
|
|
||||||
|
this.settings = new Settings({logger: this.logger});
|
||||||
|
await this.settings.init();
|
||||||
|
this.comms = new CommsServer(this);
|
||||||
|
this.comms.subscribe('show-logger', async () => await this.initUiAndShowLogger());
|
||||||
|
this.comms.subscribe('init-vue', async () => await this.initUi());
|
||||||
|
this.comms.subscribe('uwui-vue-initialized', () => this.uiLoggerInitialized = true);
|
||||||
|
this.comms.subscribe('emit-logs', () => {}); // we don't need to do anything, this gets forwarded to UI content script as is
|
||||||
|
|
||||||
|
browser.tabs.onActivated.addListener((m) => {this.onTabSwitched(m)});
|
||||||
|
}
|
||||||
|
|
||||||
|
async _promisifyTabsGet(browserObj, tabId){
|
||||||
|
return new Promise( (resolve, reject) => {
|
||||||
|
browserObj.tabs.get(tabId, (tab) => resolve(tab));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async injectCss(css, sender) {
|
||||||
|
try {
|
||||||
|
if (BrowserDetect.firefox || BrowserDetect.edge) {
|
||||||
|
browser.tabs.insertCSS(sender.tab.id, {code: css, cssOrigin: 'user', frameId: sender.frameId});
|
||||||
|
} else if (BrowserDetect.anyChromium) {
|
||||||
|
chrome.tabs.insertCSS(sender.tab.id, {code: css, cssOrigin: 'user', frameId: sender.frameId});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.log('error','debug', '[UwServer::injectCss] Error while injecting css:', {error: e, css, sender});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async removeCss(css, sender) {
|
||||||
|
try {
|
||||||
|
browser.tabs.removeCSS(sender.tab.id, {code: css, cssOrigin: 'user', frameId: sender.frameId});
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.log('error','debug', '[UwServer::injectCss] Error while removing css:', {error: e, css, sender});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async replaceCss(oldCss, newCss, sender) {
|
||||||
|
if (oldCss !== newCss) {
|
||||||
|
this.injectCss(newCss, sender);
|
||||||
|
this.removeCss(oldCss, sender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extractHostname(url){
|
||||||
|
var hostname;
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
return "<no url>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract hostname
|
||||||
|
if (url.indexOf("://") > -1) { //find & remove protocol (http, ftp, etc.) and get hostname
|
||||||
|
hostname = url.split('/')[2];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hostname = url.split('/')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
hostname = hostname.split(':')[0]; //find & remove port number
|
||||||
|
hostname = hostname.split('?')[0]; //find & remove "?"
|
||||||
|
|
||||||
|
return hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
async onTabSwitched(activeInfo){
|
||||||
|
this.hasVideos = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.currentTabId = activeInfo.tabId; // just for readability
|
||||||
|
|
||||||
|
let tab;
|
||||||
|
if (BrowserDetect.firefox) {
|
||||||
|
tab = await browser.tabs.get(this.currentTabId);
|
||||||
|
} else if (BrowserDetect.anyChromium) {
|
||||||
|
tab = await this._promisifyTabsGet(chrome, this.currentTabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentSite = this.extractHostname(tab.url);
|
||||||
|
this.logger.log('info', 'debug', '[UwServer::onTabSwitched] user switched tab. New site:', this.currentSite);
|
||||||
|
} catch(e) {
|
||||||
|
this.logger.log('error', 'debug', '[UwServer::onTabSwitched] there was a problem getting currnet site:', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectedSubitem = {
|
||||||
|
'siteSettings': undefined,
|
||||||
|
'videoSettings': undefined,
|
||||||
|
}
|
||||||
|
//TODO: change extension icon based on whether there's any videos on current page
|
||||||
|
}
|
||||||
|
|
||||||
|
registerVideo(sender) {
|
||||||
|
this.logger.log('info', 'comms', '[UWServer::registerVideo] Registering video.\nsender:', sender);
|
||||||
|
|
||||||
|
const tabHostname = this.extractHostname(sender.tab.url);
|
||||||
|
const frameHostname = this.extractHostname(sender.url);
|
||||||
|
|
||||||
|
// preveri za osirotele/zastarele vrednosti ter jih po potrebi izbriši
|
||||||
|
// check for orphaned/outdated values and remove them if neccessary
|
||||||
|
if (this.videoTabs[sender.tab.id]?.host != tabHostname) {
|
||||||
|
delete this.videoTabs[sender.tab.id]
|
||||||
|
} else if(this.videoTabs[sender.tab.id]?.frames[sender.frameId]?.host != frameHostname) {
|
||||||
|
delete this.videoTabs[sender.tab.id].frames[sender.frameId];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.videoTabs[sender.tab.id]) {
|
||||||
|
this.videoTabs[sender.tab.id].frames[sender.frameId] = {
|
||||||
|
id: sender.frameId,
|
||||||
|
host: frameHostname,
|
||||||
|
url: sender.url,
|
||||||
|
registerTime: Date.now(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.videoTabs[sender.tab.id] = {
|
||||||
|
id: sender.tab.id,
|
||||||
|
host: tabHostname,
|
||||||
|
url: sender.tab.url,
|
||||||
|
frames: {}
|
||||||
|
};
|
||||||
|
this.videoTabs[sender.tab.id].frames[sender.frameId] = {
|
||||||
|
id: sender.frameId,
|
||||||
|
host: frameHostname,
|
||||||
|
url: sender.url,
|
||||||
|
registerTime: Date.now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.log('info', 'comms', '[UWServer::registerVideo] Video registered. current videoTabs:', this.videoTabs);
|
||||||
|
}
|
||||||
|
|
||||||
|
unregisterVideo(sender) {
|
||||||
|
this.logger.log('info', 'comms', '[UwServer::unregisterVideo] Unregistering video.\nsender:', sender);
|
||||||
|
if (this.videoTabs[sender.tab.id]) {
|
||||||
|
if ( Object.keys(this.videoTabs[sender.tab.id].frames).length <= 1) {
|
||||||
|
delete this.videoTabs[sender.tab.id]
|
||||||
|
} else {
|
||||||
|
if(this.videoTabs[sender.tab.id].frames[sender.frameId]) {
|
||||||
|
delete this.videoTabs[sender.tab.id].frames[sender.frameId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.logger.log('info', 'comms', '[UwServer::unregisterVideo] Video has been unregistered. Current videoTabs:', this.videoTabs);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedTab(menu, subitem) {
|
||||||
|
this.logger.log('info', 'comms', '[UwServer::setSelectedTab] saving selected tab for', menu, ':', subitem);
|
||||||
|
this.selectedSubitem[menu] = subitem;
|
||||||
|
}
|
||||||
|
|
||||||
|
async initUi() {
|
||||||
|
try {
|
||||||
|
if (BrowserDetect.firefox) {
|
||||||
|
await browser.tabs.executeScript({
|
||||||
|
file: '/ext/uw-ui.js',
|
||||||
|
allFrames: true,
|
||||||
|
});
|
||||||
|
} else if (BrowserDetect.anyChromium) {
|
||||||
|
await new Promise<void>( resolve =>
|
||||||
|
chrome.tabs.executeScript({
|
||||||
|
file: '/ext/uw-ui.js',
|
||||||
|
allFrames: true,
|
||||||
|
}, () => resolve())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.log('ERROR', 'uwbg', 'UI initialization failed. Reason:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async initUiAndShowLogger() {
|
||||||
|
// this implementation is less than optimal and very hacky, but it should work
|
||||||
|
// just fine for our use case.
|
||||||
|
this.uiLoggerInitialized = false;
|
||||||
|
|
||||||
|
await this.initUi();
|
||||||
|
|
||||||
|
await new Promise<void>( async (resolve, reject) => {
|
||||||
|
// if content script doesn't give us a response within 5 seconds, something is
|
||||||
|
// obviously wrong and we stop waiting,
|
||||||
|
|
||||||
|
// oh and btw, resolve/reject do not break the loops, so we need to do that
|
||||||
|
// ourselves:
|
||||||
|
// https://stackoverflow.com/questions/55207256/will-resolve-in-promise-loop-break-loop-iteration
|
||||||
|
let isRejected = false;
|
||||||
|
setTimeout( async () => {isRejected = true; reject()}, 5000);
|
||||||
|
|
||||||
|
// check whether UI has been initiated on the FE. If it was, we resolve the
|
||||||
|
// promise and off we go
|
||||||
|
while (!isRejected) {
|
||||||
|
if (this.uiLoggerInitialized) {
|
||||||
|
resolve();
|
||||||
|
return; // remember the bit about resolve() not breaking the loop?
|
||||||
|
}
|
||||||
|
await sleep(100);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCurrentTab() {
|
||||||
|
return (await browser.tabs.query({active: true, currentWindow: true}))[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
async getVideoTab() {
|
||||||
|
// friendly reminder: if current tab doesn't have a video,
|
||||||
|
// there won't be anything in this.videoTabs[this.currentTabId]
|
||||||
|
|
||||||
|
const ctab = await this.getCurrentTab();
|
||||||
|
|
||||||
|
if (!ctab || !ctab.id) {
|
||||||
|
return {
|
||||||
|
host: 'INVALID SITE',
|
||||||
|
frames: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.videoTabs[ctab.id]) {
|
||||||
|
// if video is older than PageInfo's video rescan period (+ 4000ms of grace),
|
||||||
|
// we clean it up from videoTabs[tabId].frames array.
|
||||||
|
const ageLimit = Date.now() - this.settings.active.pageInfo.timeouts.rescan - 4000;
|
||||||
|
try {
|
||||||
|
for (const key in this.videoTabs[ctab.id].frames) {
|
||||||
|
if (this.videoTabs[ctab.id].frames[key].registerTime < ageLimit) {
|
||||||
|
delete this.videoTabs[ctab.id].frames[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// something went wrong. There's prolly no frames.
|
||||||
|
return {
|
||||||
|
host: this.extractHostname(ctab.url),
|
||||||
|
frames: [],
|
||||||
|
selected: this.selectedSubitem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...this.videoTabs[ctab.id],
|
||||||
|
host: this.extractHostname(ctab.url),
|
||||||
|
selected: this.selectedSubitem
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// return something more or less empty if this tab doesn't have
|
||||||
|
// a video registered for it
|
||||||
|
return {
|
||||||
|
host: this.extractHostname(ctab.url),
|
||||||
|
frames: [],
|
||||||
|
selected: this.selectedSubitem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// chrome shitiness mitigation
|
||||||
|
sendUnmarkPlayer(message) {
|
||||||
|
this.comms.sendUnmarkPlayer(message);
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +1,27 @@
|
|||||||
import VideoAlignment from '../../common/enums/video-alignment.enum';
|
import VideoAlignmentType from '../../common/enums/VideoAlignmentType.enum';
|
||||||
import Stretch from '../../common/enums/stretch.enum';
|
import StretchType from '../../common/enums/StretchType.enum';
|
||||||
import ExtensionMode from '../../common/enums/extension-mode.enum';
|
import ExtensionMode from '../../common/enums/ExtensionMode.enum';
|
||||||
import AspectRatio from '../../common/enums/aspect-ratio.enum';
|
import AspectRatioType from '../../common/enums/AspectRatioType.enum';
|
||||||
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
|
import CropModePersistence from '../../common/enums/CropModePersistence.enum';
|
||||||
|
|
||||||
var ActionList = {
|
var ActionList = {
|
||||||
'set-ar': {
|
'set-ar': {
|
||||||
name: 'Set aspect ratio',
|
name: 'Set aspect ratio',
|
||||||
args: [{
|
args: [{
|
||||||
name: 'Automatic',
|
name: 'Automatic',
|
||||||
arg: AspectRatio.Automatic,
|
arg: AspectRatioType.Automatic,
|
||||||
},{
|
},{
|
||||||
name: 'Fit width',
|
name: 'Fit width',
|
||||||
arg: AspectRatio.FitWidth,
|
arg: AspectRatioType.FitWidth,
|
||||||
},{
|
},{
|
||||||
name: 'Fit height',
|
name: 'Fit height',
|
||||||
arg: AspectRatio.FitHeight,
|
arg: AspectRatioType.FitHeight,
|
||||||
},{
|
},{
|
||||||
name: 'Reset',
|
name: 'Reset',
|
||||||
arg: AspectRatio.Reset,
|
arg: AspectRatioType.Reset,
|
||||||
},{
|
},{
|
||||||
name: 'Manually specify ratio',
|
name: 'Manually specify ratio',
|
||||||
arg: AspectRatio.Fixed,
|
arg: AspectRatioType.Fixed,
|
||||||
customArg: true,
|
customArg: true,
|
||||||
customSetter: (value) => {
|
customSetter: (value) => {
|
||||||
const [width, height] = value.split(':');
|
const [width, height] = value.split(':');
|
||||||
@ -70,33 +70,33 @@ var ActionList = {
|
|||||||
name: 'Set stretch',
|
name: 'Set stretch',
|
||||||
args: [{
|
args: [{
|
||||||
name: 'Normal',
|
name: 'Normal',
|
||||||
arg: Stretch.NoStretch
|
arg: StretchType.NoStretch
|
||||||
},{
|
},{
|
||||||
name: 'Basic',
|
name: 'Basic',
|
||||||
arg: Stretch.Basic,
|
arg: StretchType.Basic,
|
||||||
},{
|
},{
|
||||||
name: 'Hybrid',
|
name: 'Hybrid',
|
||||||
arg: Stretch.Hybrid,
|
arg: StretchType.Hybrid,
|
||||||
},{
|
},{
|
||||||
name: 'Thin borders',
|
name: 'Thin borders',
|
||||||
arg: Stretch.Conditional,
|
arg: StretchType.Conditional,
|
||||||
},{
|
},{
|
||||||
name: 'Fixed (source)',
|
name: 'Fixed (source)',
|
||||||
arg: Stretch.FixedSource,
|
arg: StretchType.FixedSource,
|
||||||
customArg: true,
|
customArg: true,
|
||||||
scopes: {
|
scopes: {
|
||||||
page: true,
|
page: true,
|
||||||
}
|
}
|
||||||
},{
|
},{
|
||||||
name: 'Fixed (displayed)',
|
name: 'Fixed (displayed)',
|
||||||
arg: Stretch.Fixed,
|
arg: StretchType.Fixed,
|
||||||
customArg: true,
|
customArg: true,
|
||||||
scopes: {
|
scopes: {
|
||||||
page: true,
|
page: true,
|
||||||
}
|
}
|
||||||
},{
|
},{
|
||||||
name: 'Default',
|
name: 'Default',
|
||||||
arg: Stretch.Default,
|
arg: StretchType.Default,
|
||||||
scopes: {
|
scopes: {
|
||||||
site: true
|
site: true
|
||||||
}
|
}
|
||||||
@ -111,16 +111,16 @@ var ActionList = {
|
|||||||
name: 'Set video alignment',
|
name: 'Set video alignment',
|
||||||
args: [{
|
args: [{
|
||||||
name: 'Left',
|
name: 'Left',
|
||||||
arg: VideoAlignment.Left,
|
arg: VideoAlignmentType.Left,
|
||||||
},{
|
},{
|
||||||
name: 'Center',
|
name: 'Center',
|
||||||
arg: VideoAlignment.Center,
|
arg: VideoAlignmentType.Center,
|
||||||
},{
|
},{
|
||||||
name: 'Right',
|
name: 'Right',
|
||||||
arg: VideoAlignment.Right
|
arg: VideoAlignmentType.Right
|
||||||
},{
|
},{
|
||||||
name: 'Default',
|
name: 'Default',
|
||||||
arg: VideoAlignment.Default,
|
arg: VideoAlignmentType.Default,
|
||||||
scopes: {
|
scopes: {
|
||||||
site: true,
|
site: true,
|
||||||
}
|
}
|
||||||
@ -179,7 +179,7 @@ var ActionList = {
|
|||||||
page: true,
|
page: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'set-extension-mode': {
|
'set-ExtensionMode': {
|
||||||
name: 'Set extension mode',
|
name: 'Set extension mode',
|
||||||
args: [{
|
args: [{
|
||||||
name: 'Enable',
|
name: 'Enable',
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// How to use:
|
// How to use:
|
||||||
// version: {ExtensionConf object, but only properties that get overwritten}
|
// version: {ExtensionConf object, but only properties that get overwritten}
|
||||||
import Stretch from '../../common/enums/stretch.enum';
|
import StretchType from '../../common/enums/StretchType.enum';
|
||||||
import ExtensionMode from '../../common/enums/extension-mode.enum';
|
import ExtensionMode from '../../common/enums/ExtensionMode.enum';
|
||||||
import VideoAlignment from '../../common/enums/video-alignment.enum';
|
import VideoAlignmentType from '../../common/enums/VideoAlignmentType.enum';
|
||||||
|
|
||||||
const ExtensionConfPatch = [
|
const ExtensionConfPatch = [
|
||||||
{
|
{
|
||||||
@ -267,7 +267,7 @@ const ExtensionConfPatch = [
|
|||||||
label: '4:3 stretch (src)',
|
label: '4:3 stretch (src)',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-stretch',
|
action: 'set-stretch',
|
||||||
arg: Stretch.FixedSource,
|
arg: StretchType.FixedSource,
|
||||||
customArg: 1.33,
|
customArg: 1.33,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
@ -284,7 +284,7 @@ const ExtensionConfPatch = [
|
|||||||
label: '16:9 stretch (src)',
|
label: '16:9 stretch (src)',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-stretch',
|
action: 'set-stretch',
|
||||||
arg: Stretch.FixedSource,
|
arg: StretchType.FixedSource,
|
||||||
customArg: 1.77,
|
customArg: 1.77,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
@ -309,8 +309,8 @@ const ExtensionConfPatch = [
|
|||||||
autoar: ExtensionMode.Enabled,
|
autoar: ExtensionMode.Enabled,
|
||||||
autoarFallback: ExtensionMode.Enabled,
|
autoarFallback: ExtensionMode.Enabled,
|
||||||
override: true, // ignore value localStorage in favour of this
|
override: true, // ignore value localStorage in favour of this
|
||||||
stretch: Stretch.Default,
|
stretch: StretchType.Default,
|
||||||
videoAlignment: VideoAlignment.Default,
|
videoAlignment: VideoAlignmentType.Default,
|
||||||
keyboardShortcutsEnabled: ExtensionMode.Default,
|
keyboardShortcutsEnabled: ExtensionMode.Default,
|
||||||
DOM: {
|
DOM: {
|
||||||
player: {
|
player: {
|
||||||
@ -339,8 +339,8 @@ const ExtensionConfPatch = [
|
|||||||
autoar: ExtensionMode.Enabled,
|
autoar: ExtensionMode.Enabled,
|
||||||
override: false,
|
override: false,
|
||||||
type: 'community',
|
type: 'community',
|
||||||
stretch: Stretch.Default,
|
stretch: StretchType.Default,
|
||||||
videoAlignment: VideoAlignment.Default,
|
videoAlignment: VideoAlignmentType.Default,
|
||||||
keyboardShortcutsEnabled: ExtensionMode.Default,
|
keyboardShortcutsEnabled: ExtensionMode.Default,
|
||||||
arPersistence: true, // persist aspect ratio between different videos
|
arPersistence: true, // persist aspect ratio between different videos
|
||||||
autoarPreventConditions: { // prevents autoar on following conditions
|
autoarPreventConditions: { // prevents autoar on following conditions
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import Debug from './Debug';
|
import Debug from './Debug';
|
||||||
import currentBrowser from './BrowserDetect';
|
import currentBrowser from './BrowserDetect';
|
||||||
import VideoAlignment from '../../common/enums/video-alignment.enum';
|
import VideoAlignmentType from '../../common/enums/VideoAlignmentType.enum';
|
||||||
import Stretch from '../../common/enums/stretch.enum';
|
import StretchType from '../../common/enums/StretchType.enum';
|
||||||
import ExtensionMode from '../../common/enums/extension-mode.enum';
|
import ExtensionMode from '../../common/enums/ExtensionMode.enum';
|
||||||
import AntiGradientMode from '../../common/enums/anti-gradient-mode.enum';
|
import AntiGradientMode from '../../common/enums/AntiGradientMode.enum';
|
||||||
import AspectRatio from '../../common/enums/aspect-ratio.enum';
|
import AspectRatioType from '../../common/enums/AspectRatioType.enum';
|
||||||
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
|
import CropModePersistence from '../../common/enums/CropModePersistence.enum';
|
||||||
|
import SettingsInterface from '../../common/interfaces/SettingsInterface';
|
||||||
|
|
||||||
if(Debug.debug)
|
if(Debug.debug)
|
||||||
console.log("Loading: ExtensionConf.js");
|
console.log("Loading: ExtensionConf.js");
|
||||||
|
|
||||||
var ExtensionConf = {
|
const ExtensionConf: SettingsInterface = {
|
||||||
arDetect: {
|
arDetect: {
|
||||||
disabledReason: "", // if automatic aspect ratio has been disabled, show reason
|
disabledReason: "", // if automatic aspect ratio has been disabled, show reason
|
||||||
allowedMisaligned: 0.05, // top and bottom letterbox thickness can differ by this much.
|
allowedMisaligned: 0.05, // top and bottom letterbox thickness can differ by this much.
|
||||||
@ -176,7 +177,7 @@ var ExtensionConf = {
|
|||||||
label: 'Automatic', // name displayed in ui (can be overridden in scope/playerUi)
|
label: 'Automatic', // name displayed in ui (can be overridden in scope/playerUi)
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-ar',
|
action: 'set-ar',
|
||||||
arg: AspectRatio.Automatic,
|
arg: AspectRatioType.Automatic,
|
||||||
persistent: false, // optional, false by default. If true, change doesn't take effect immediately.
|
persistent: false, // optional, false by default. If true, change doesn't take effect immediately.
|
||||||
// Instead, this action saves stuff to settings
|
// Instead, this action saves stuff to settings
|
||||||
}],
|
}],
|
||||||
@ -211,7 +212,7 @@ var ExtensionConf = {
|
|||||||
label: 'Reset',
|
label: 'Reset',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-ar',
|
action: 'set-ar',
|
||||||
arg: AspectRatio.Reset,
|
arg: AspectRatioType.Reset,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
page: {
|
page: {
|
||||||
@ -237,7 +238,7 @@ var ExtensionConf = {
|
|||||||
label: 'Fit width',
|
label: 'Fit width',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-ar',
|
action: 'set-ar',
|
||||||
arg: AspectRatio.FitWidth,
|
arg: AspectRatioType.FitWidth,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
page: {
|
page: {
|
||||||
@ -263,7 +264,7 @@ var ExtensionConf = {
|
|||||||
label: 'Fit height',
|
label: 'Fit height',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-ar',
|
action: 'set-ar',
|
||||||
arg: AspectRatio.FitHeight
|
arg: AspectRatioType.FitHeight
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
page: {
|
page: {
|
||||||
@ -290,7 +291,7 @@ var ExtensionConf = {
|
|||||||
label: '16:9',
|
label: '16:9',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-ar',
|
action: 'set-ar',
|
||||||
arg: AspectRatio.Fixed,
|
arg: AspectRatioType.Fixed,
|
||||||
customArg: 1.78,
|
customArg: 1.78,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
@ -318,7 +319,7 @@ var ExtensionConf = {
|
|||||||
label: '21:9',
|
label: '21:9',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-ar',
|
action: 'set-ar',
|
||||||
arg: AspectRatio.Fixed,
|
arg: AspectRatioType.Fixed,
|
||||||
customArg: 2.39
|
customArg: 2.39
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
@ -346,7 +347,7 @@ var ExtensionConf = {
|
|||||||
label: '18:9',
|
label: '18:9',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-ar',
|
action: 'set-ar',
|
||||||
arg: AspectRatio.Fixed,
|
arg: AspectRatioType.Fixed,
|
||||||
customArg: 2.0,
|
customArg: 2.0,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
@ -373,7 +374,7 @@ var ExtensionConf = {
|
|||||||
label: 'Never persist',
|
label: 'Never persist',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-ar-persistence',
|
action: 'set-ar-persistence',
|
||||||
arg: CropModePersistence.Never,
|
arg: CropModePersistence.Disabled,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
site: {
|
site: {
|
||||||
@ -547,7 +548,7 @@ var ExtensionConf = {
|
|||||||
label: 'Don\'t stretch',
|
label: 'Don\'t stretch',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-stretch',
|
action: 'set-stretch',
|
||||||
arg: Stretch.NoStretch,
|
arg: StretchType.NoStretch,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
global: {
|
global: {
|
||||||
@ -572,7 +573,7 @@ var ExtensionConf = {
|
|||||||
label: 'Basic stretch',
|
label: 'Basic stretch',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-stretch',
|
action: 'set-stretch',
|
||||||
arg: Stretch.Basic,
|
arg: StretchType.Basic,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
global: {
|
global: {
|
||||||
@ -597,7 +598,7 @@ var ExtensionConf = {
|
|||||||
label: 'Hybrid stretch',
|
label: 'Hybrid stretch',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-stretch',
|
action: 'set-stretch',
|
||||||
arg: Stretch.Hybrid,
|
arg: StretchType.Hybrid,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
global: {
|
global: {
|
||||||
@ -622,7 +623,7 @@ var ExtensionConf = {
|
|||||||
label: 'Thin borders only',
|
label: 'Thin borders only',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-stretch',
|
action: 'set-stretch',
|
||||||
arg: Stretch.Conditional,
|
arg: StretchType.Conditional,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
global: {
|
global: {
|
||||||
@ -647,7 +648,7 @@ var ExtensionConf = {
|
|||||||
label: 'Default',
|
label: 'Default',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-stretch',
|
action: 'set-stretch',
|
||||||
arg: Stretch.Default,
|
arg: StretchType.Default,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
site: {
|
site: {
|
||||||
@ -660,7 +661,7 @@ var ExtensionConf = {
|
|||||||
label: '4:3 stretch (src)',
|
label: '4:3 stretch (src)',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-stretch',
|
action: 'set-stretch',
|
||||||
arg: Stretch.FixedSource,
|
arg: StretchType.FixedSource,
|
||||||
customArg: 1.33,
|
customArg: 1.33,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
@ -677,7 +678,7 @@ var ExtensionConf = {
|
|||||||
label: '16:9 stretch (src)',
|
label: '16:9 stretch (src)',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-stretch',
|
action: 'set-stretch',
|
||||||
arg: Stretch.FixedSource,
|
arg: StretchType.FixedSource,
|
||||||
customArg: 1.77,
|
customArg: 1.77,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
@ -698,7 +699,7 @@ var ExtensionConf = {
|
|||||||
label: 'Left',
|
label: 'Left',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-alignment',
|
action: 'set-alignment',
|
||||||
arg: VideoAlignment.Left,
|
arg: VideoAlignmentType.Left,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
global: {
|
global: {
|
||||||
@ -720,7 +721,7 @@ var ExtensionConf = {
|
|||||||
label: 'Center',
|
label: 'Center',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-alignment',
|
action: 'set-alignment',
|
||||||
arg: VideoAlignment.Center,
|
arg: VideoAlignmentType.Center,
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
global: {
|
global: {
|
||||||
@ -742,7 +743,7 @@ var ExtensionConf = {
|
|||||||
label: 'Right',
|
label: 'Right',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-alignment',
|
action: 'set-alignment',
|
||||||
arg: VideoAlignment.Right
|
arg: VideoAlignmentType.Right
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
global: {
|
global: {
|
||||||
@ -764,7 +765,7 @@ var ExtensionConf = {
|
|||||||
label: 'Default',
|
label: 'Default',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-alignment',
|
action: 'set-alignment',
|
||||||
arg: VideoAlignment.Default
|
arg: VideoAlignmentType.Default
|
||||||
}],
|
}],
|
||||||
scopes: {
|
scopes: {
|
||||||
site: {
|
site: {
|
||||||
@ -780,7 +781,7 @@ var ExtensionConf = {
|
|||||||
name: 'Enable extension',
|
name: 'Enable extension',
|
||||||
label: 'Enable',
|
label: 'Enable',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-extension-mode',
|
action: 'set-ExtensionMode',
|
||||||
arg: ExtensionMode.Enabled,
|
arg: ExtensionMode.Enabled,
|
||||||
persistent: true,
|
persistent: true,
|
||||||
}],
|
}],
|
||||||
@ -796,7 +797,7 @@ var ExtensionConf = {
|
|||||||
name: 'Enable extension on whitelisted sites only',
|
name: 'Enable extension on whitelisted sites only',
|
||||||
label: 'On whitelist only',
|
label: 'On whitelist only',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-extension-mode',
|
action: 'set-ExtensionMode',
|
||||||
arg: ExtensionMode.Whitelist,
|
arg: ExtensionMode.Whitelist,
|
||||||
persistent: true,
|
persistent: true,
|
||||||
}],
|
}],
|
||||||
@ -809,7 +810,7 @@ var ExtensionConf = {
|
|||||||
name: 'Extension mode: use default settings',
|
name: 'Extension mode: use default settings',
|
||||||
label: 'Default',
|
label: 'Default',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-extension-mode',
|
action: 'set-ExtensionMode',
|
||||||
arg: ExtensionMode.Default,
|
arg: ExtensionMode.Default,
|
||||||
persistent: true,
|
persistent: true,
|
||||||
}],
|
}],
|
||||||
@ -822,7 +823,7 @@ var ExtensionConf = {
|
|||||||
name: 'Disable extension',
|
name: 'Disable extension',
|
||||||
label: 'Disable',
|
label: 'Disable',
|
||||||
cmd: [{
|
cmd: [{
|
||||||
action: 'set-extension-mode',
|
action: 'set-ExtensionMode',
|
||||||
arg: ExtensionMode.Disabled,
|
arg: ExtensionMode.Disabled,
|
||||||
persistent: true,
|
persistent: true,
|
||||||
}],
|
}],
|
||||||
@ -1006,8 +1007,8 @@ var ExtensionConf = {
|
|||||||
autoarFallback: currentBrowser.firefox ? // if autoAr fails, try fallback mode?
|
autoarFallback: currentBrowser.firefox ? // if autoAr fails, try fallback mode?
|
||||||
ExtensionMode.Enabled : // Options same as in autoar.
|
ExtensionMode.Enabled : // Options same as in autoar.
|
||||||
ExtensionMode.Disabled, // if autoar is disabled, this setting is irrelevant
|
ExtensionMode.Disabled, // if autoar is disabled, this setting is irrelevant
|
||||||
stretch: Stretch.NoStretch, // Default stretch mode.
|
stretch: StretchType.NoStretch, // Default stretch mode.
|
||||||
videoAlignment: VideoAlignment.Center, // Video alignment
|
videoAlignment: VideoAlignmentType.Center, // Video alignment
|
||||||
keyboardShortcutsEnabled: ExtensionMode.Enabled,
|
keyboardShortcutsEnabled: ExtensionMode.Enabled,
|
||||||
},
|
},
|
||||||
"www.youtube.com" : {
|
"www.youtube.com" : {
|
||||||
@ -1017,8 +1018,8 @@ var ExtensionConf = {
|
|||||||
override: false, // ignore value localStorage in favour of this
|
override: false, // ignore value localStorage in favour of this
|
||||||
type: 'official', // is officially supported? (Alternatives are 'community' and 'user-defined')
|
type: 'official', // is officially supported? (Alternatives are 'community' and 'user-defined')
|
||||||
actions: null, // overrides global keyboard shortcuts and button configs. Is array, is optional.
|
actions: null, // overrides global keyboard shortcuts and button configs. Is array, is optional.
|
||||||
stretch: Stretch.Default,
|
stretch: StretchType.Default,
|
||||||
videoAlignment: VideoAlignment.Default,
|
videoAlignment: VideoAlignmentType.Default,
|
||||||
keyboardShortcutsEnabled: ExtensionMode.Default,
|
keyboardShortcutsEnabled: ExtensionMode.Default,
|
||||||
DOM: {
|
DOM: {
|
||||||
player: {
|
player: {
|
||||||
@ -1035,8 +1036,8 @@ var ExtensionConf = {
|
|||||||
autoar: ExtensionMode.Enabled,
|
autoar: ExtensionMode.Enabled,
|
||||||
override: false,
|
override: false,
|
||||||
type: 'official',
|
type: 'official',
|
||||||
stretch: Stretch.Default,
|
stretch: StretchType.Default,
|
||||||
videoAlignment: VideoAlignment.Default,
|
videoAlignment: VideoAlignmentType.Default,
|
||||||
keyboardShortcutsEnabled: ExtensionMode.Default,
|
keyboardShortcutsEnabled: ExtensionMode.Default,
|
||||||
arPersistence: true, // persist aspect ratio between different videos
|
arPersistence: true, // persist aspect ratio between different videos
|
||||||
"DOM": {
|
"DOM": {
|
||||||
@ -1054,8 +1055,8 @@ var ExtensionConf = {
|
|||||||
autoar: ExtensionMode.Enabled,
|
autoar: ExtensionMode.Enabled,
|
||||||
override: false,
|
override: false,
|
||||||
type: 'community',
|
type: 'community',
|
||||||
stretch: Stretch.Default,
|
stretch: StretchType.Default,
|
||||||
videoAlignment: VideoAlignment.Default,
|
videoAlignment: VideoAlignmentType.Default,
|
||||||
keyboardShortcutsEnabled: ExtensionMode.Default,
|
keyboardShortcutsEnabled: ExtensionMode.Default,
|
||||||
arPersistence: true, // persist aspect ratio between different videos
|
arPersistence: true, // persist aspect ratio between different videos
|
||||||
DOM: {
|
DOM: {
|
||||||
@ -1074,8 +1075,8 @@ var ExtensionConf = {
|
|||||||
autoar: ExtensionMode.Enabled,
|
autoar: ExtensionMode.Enabled,
|
||||||
override: true,
|
override: true,
|
||||||
type: 'official',
|
type: 'official',
|
||||||
stretch: Stretch.Default,
|
stretch: StretchType.Default,
|
||||||
videoAlignment: VideoAlignment.Default,
|
videoAlignment: VideoAlignmentType.Default,
|
||||||
keyboardShortcutsEnabled: ExtensionMode.Default,
|
keyboardShortcutsEnabled: ExtensionMode.Default,
|
||||||
DOM: {
|
DOM: {
|
||||||
player: {
|
player: {
|
||||||
@ -1117,8 +1118,8 @@ var ExtensionConf = {
|
|||||||
autoar:ExtensionMode.Enabled,
|
autoar:ExtensionMode.Enabled,
|
||||||
override: false,
|
override: false,
|
||||||
type: 'testing',
|
type: 'testing',
|
||||||
stretch: Stretch.Default,
|
stretch: StretchType.Default,
|
||||||
videoAlignment: VideoAlignment.Default,
|
videoAlignment: VideoAlignmentType.Default,
|
||||||
keyboardShortcutsEnabled: ExtensionMode.Default,
|
keyboardShortcutsEnabled: ExtensionMode.Default,
|
||||||
DOM: {
|
DOM: {
|
||||||
player: {
|
player: {
|
||||||
@ -1134,8 +1135,8 @@ var ExtensionConf = {
|
|||||||
autoar: ExtensionMode.Enabled,
|
autoar: ExtensionMode.Enabled,
|
||||||
override: false,
|
override: false,
|
||||||
type: 'testing',
|
type: 'testing',
|
||||||
stretch: Stretch.Default,
|
stretch: StretchType.Default,
|
||||||
videoAlignment: VideoAlignment.Default,
|
videoAlignment: VideoAlignmentType.Default,
|
||||||
keyboardShortcutsEnabled: ExtensionMode.Default,
|
keyboardShortcutsEnabled: ExtensionMode.Default,
|
||||||
DOM: {
|
DOM: {
|
||||||
player: {
|
player: {
|
@ -1,20 +1,37 @@
|
|||||||
import Debug from '../conf/Debug';
|
import Debug from '../conf/Debug';
|
||||||
import PlayerData from './video-data/PlayerData';
|
import PlayerData from './video-data/PlayerData';
|
||||||
import ExtensionMode from '../../common/enums/extension-mode.enum';
|
import ExtensionMode from '../../common/enums/ExtensionMode.enum';
|
||||||
|
import Logger from './Logger';
|
||||||
|
import PageInfo from './video-data/PageInfo';
|
||||||
|
import Settings from './Settings';
|
||||||
|
import VideoData from './video-data/VideoData';
|
||||||
|
|
||||||
if(process.env.CHANNEL !== 'stable'){
|
if(process.env.CHANNEL !== 'stable'){
|
||||||
console.info("Loading ActionHandler");
|
console.info("Loading ActionHandler");
|
||||||
}
|
}
|
||||||
|
|
||||||
class ActionHandler {
|
class ActionHandler {
|
||||||
|
logger: Logger;
|
||||||
|
pageInfo: PageInfo;
|
||||||
|
settings: Settings;
|
||||||
|
|
||||||
|
|
||||||
|
inputs: string[] = ['input', 'select', 'button', 'textarea'];
|
||||||
|
keyboardLocalDisabled: boolean = false;
|
||||||
|
|
||||||
|
keyUpActions: any[] = [];
|
||||||
|
keyDownActions: any[] = [];
|
||||||
|
mouseMoveActions: any[] = [];
|
||||||
|
mouseScrollUpActions: any[] = [];
|
||||||
|
mouseScrollDownActions: any[] = [];
|
||||||
|
mouseEnterActions: any[] = [];
|
||||||
|
mouseLeaveActions: any[] = [];
|
||||||
|
|
||||||
|
|
||||||
constructor(pageInfo) {
|
constructor(pageInfo) {
|
||||||
this.logger = pageInfo.logger;
|
this.logger = pageInfo.logger;
|
||||||
this.pageInfo = pageInfo;
|
this.pageInfo = pageInfo;
|
||||||
this.settings = pageInfo.settings;
|
this.settings = pageInfo.settings;
|
||||||
|
|
||||||
this.inputs = ['input', 'select', 'button', 'textarea'];
|
|
||||||
this.keyboardLocalDisabled = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
@ -105,7 +122,7 @@ class ActionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// events should be handled in handleEvent function. We need to do things this
|
// events should be handled in handleEvent function. We need to do things this
|
||||||
// way, otherwise we can't remove event listenerđ
|
// way, otherwise we can't remove event listener
|
||||||
// https://stackoverflow.com/a/19507086
|
// https://stackoverflow.com/a/19507086
|
||||||
document.addEventListener('keydown', this );
|
document.addEventListener('keydown', this );
|
||||||
document.addEventListener('keyup', this );
|
document.addEventListener('keyup', this );
|
||||||
@ -168,7 +185,6 @@ class ActionHandler {
|
|||||||
this.logger.resume(); // undisable
|
this.logger.resume(); // undisable
|
||||||
|
|
||||||
this.logger.log('info', 'keyboard', "[ActionHandler::preventAction] Testing whether we're in a textbox or something. Detailed rundown of conditions:\n" +
|
this.logger.log('info', 'keyboard', "[ActionHandler::preventAction] Testing whether we're in a textbox or something. Detailed rundown of conditions:\n" +
|
||||||
"is full screen? (yes->allow):", PlayerData.isFullScreen(),
|
|
||||||
"\nis tag one of defined inputs? (yes->prevent):", this.inputs.indexOf(activeElement.tagName.toLocaleLowerCase()) !== -1,
|
"\nis tag one of defined inputs? (yes->prevent):", this.inputs.indexOf(activeElement.tagName.toLocaleLowerCase()) !== -1,
|
||||||
"\nis role = textbox? (yes -> prevent):", activeElement.getAttribute("role") === "textbox",
|
"\nis role = textbox? (yes -> prevent):", activeElement.getAttribute("role") === "textbox",
|
||||||
"\nis type === 'text'? (yes -> prevent):", activeElement.getAttribute("type") === "text",
|
"\nis type === 'text'? (yes -> prevent):", activeElement.getAttribute("type") === "text",
|
||||||
@ -186,12 +202,6 @@ class ActionHandler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// lately youtube has allowed you to read and write comments while watching video in
|
|
||||||
// fullscreen mode. We can no longer do this.
|
|
||||||
// if (PlayerData.isFullScreen()) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (this.keyboardLocalDisabled) {
|
if (this.keyboardLocalDisabled) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -240,7 +250,7 @@ class ActionHandler {
|
|||||||
this.isActionMatchStandard(shortcut, event) || this.isActionMatchKeyCode(shortcut, event);
|
this.isActionMatchStandard(shortcut, event) || this.isActionMatchKeyCode(shortcut, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
execAction(actions, event, videoData) {
|
execAction(actions, event, videoData?: VideoData) {
|
||||||
this.logger.log('info', 'keyboard', "%c[ActionHandler::execAction] Trying to find and execute action for event. Actions/event: ", "color: #ff0", actions, event);
|
this.logger.log('info', 'keyboard', "%c[ActionHandler::execAction] Trying to find and execute action for event. Actions/event: ", "color: #ff0", actions, event);
|
||||||
|
|
||||||
const isLatin = event.key ? this.isLatin(event.key) : true;
|
const isLatin = event.key ? this.isLatin(event.key) : true;
|
||||||
@ -276,9 +286,9 @@ class ActionHandler {
|
|||||||
} else if (cmd.action === "set-alignment") {
|
} else if (cmd.action === "set-alignment") {
|
||||||
this.settings.active.sites[site].videoAlignment = cmd.arg;
|
this.settings.active.sites[site].videoAlignment = cmd.arg;
|
||||||
} else if (cmd.action === "set-extension-mode") {
|
} else if (cmd.action === "set-extension-mode") {
|
||||||
this.settings.active.sites[site].status = cmd.arg;
|
this.settings.active.sites[site].mode = cmd.arg;
|
||||||
} else if (cmd.action === "set-autoar-mode") {
|
} else if (cmd.action === "set-autoar-mode") {
|
||||||
this.settings.active.sites[site].arStatus = cmd.arg;
|
this.settings.active.sites[site].autoar = cmd.arg;
|
||||||
} else if (cmd.action === 'set-keyboard') {
|
} else if (cmd.action === 'set-keyboard') {
|
||||||
this.settings.active.sites[site].keyboardShortcutsEnabled = cmd.arg;
|
this.settings.active.sites[site].keyboardShortcutsEnabled = cmd.arg;
|
||||||
} else if (cmd.action === 'set-ar-persistence') {
|
} else if (cmd.action === 'set-ar-persistence') {
|
||||||
@ -323,9 +333,9 @@ class ActionHandler {
|
|||||||
this.execAction(this.keyDownActions, event);
|
this.execAction(this.keyDownActions, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMouseMove(event, videoData) {
|
handleMouseMove(event, videoData?: VideoData) {
|
||||||
this.logger.log('info', 'keyboard', "[ActionHandler::handleMouseMove] mouse move is being handled.\nevent:", event, "\nvideo data:", videoData);
|
this.logger.log('info', 'keyboard', "[ActionHandler::handleMouseMove] mouse move is being handled.\nevent:", event, "\nvideo data:", videoData);
|
||||||
videoData.panHandler(event);
|
videoData?.panHandler(event);
|
||||||
this.execAction(this.mouseMoveActions, event, videoData)
|
this.execAction(this.mouseMoveActions, event, videoData)
|
||||||
}
|
}
|
||||||
|
|
@ -1,42 +1,112 @@
|
|||||||
import currentBrowser from '../conf/BrowserDetect';
|
|
||||||
import { decycle } from 'json-cyclic';
|
import { decycle } from 'json-cyclic';
|
||||||
import Comms from './comms/Comms';
|
import Comms from './comms/Comms';
|
||||||
|
import BrowserDetect from '../conf/BrowserDetect';
|
||||||
|
import { browser } from 'webextension-polyfill-ts';
|
||||||
|
|
||||||
if (process.env.CHANNEL !== 'stable'){
|
if (process.env.CHANNEL !== 'stable'){
|
||||||
console.info('Loading Logger');
|
console.info('Loading Logger');
|
||||||
}
|
}
|
||||||
|
|
||||||
class Logger {
|
|
||||||
constructor(options) {
|
|
||||||
this.onLogEndCallbacks = [];
|
|
||||||
this.history = [];
|
|
||||||
this.globalHistory = {};
|
|
||||||
this.isContentScript = false;
|
|
||||||
this.isBackgroundScript = true;
|
|
||||||
|
|
||||||
|
export const baseLoggingOptions: LoggerConfig = {
|
||||||
|
isContentScript: true,
|
||||||
|
allowLogging: true,
|
||||||
|
useConfFromStorage: true,
|
||||||
|
fileOptions: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
consoleOptions: {
|
||||||
|
"enabled": true,
|
||||||
|
"debug": true,
|
||||||
|
"init": true,
|
||||||
|
"settings": true,
|
||||||
|
"keyboard": true,
|
||||||
|
"mousemove": false,
|
||||||
|
"actionHandler": true,
|
||||||
|
"comms": true,
|
||||||
|
"playerDetect": true,
|
||||||
|
"resizer": true,
|
||||||
|
"scaler": true,
|
||||||
|
"stretcher": true,
|
||||||
|
// "videoRescan": true,
|
||||||
|
// "playerRescan": true,
|
||||||
|
"arDetect": true,
|
||||||
|
"arDetect_verbose": true
|
||||||
|
},
|
||||||
|
allowBlacklistedOrigins: {
|
||||||
|
'periodicPlayerCheck': false,
|
||||||
|
'periodicVideoStyleChangeCheck': false,
|
||||||
|
'handleMouseMove': false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface LoggingOptions {
|
||||||
|
enabled?: boolean;
|
||||||
|
debug?: boolean;
|
||||||
|
init?: boolean;
|
||||||
|
settings?: boolean;
|
||||||
|
keyboard?: boolean;
|
||||||
|
mousemove?: boolean;
|
||||||
|
actionHandler?: boolean;
|
||||||
|
comms?: boolean;
|
||||||
|
playerDetect?: boolean;
|
||||||
|
resizer?: boolean;
|
||||||
|
scaler?: boolean;
|
||||||
|
stretcher?: boolean;
|
||||||
|
videoRescan?: boolean;
|
||||||
|
playerRescan?: boolean;
|
||||||
|
arDetect?: boolean;
|
||||||
|
arDetect_verbose?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LoggerBlacklistedOrigins {
|
||||||
|
periodicPlayerCheck?: boolean;
|
||||||
|
periodicVideoStyleChangeCheck?: boolean;
|
||||||
|
handleMouseMove?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LoggerConfig {
|
||||||
|
isContentScript?: boolean;
|
||||||
|
isBackgroundScript?: boolean;
|
||||||
|
allowLogging?: boolean;
|
||||||
|
useConfFromStorage?: boolean;
|
||||||
|
fileOptions?: LoggingOptions;
|
||||||
|
consoleOptions?: LoggingOptions;
|
||||||
|
allowBlacklistedOrigins?: LoggerBlacklistedOrigins;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Logger {
|
||||||
|
temp_disable: boolean = false;
|
||||||
|
onLogEndCallbacks: any[] = [];
|
||||||
|
history: any[] = [];
|
||||||
|
globalHistory: any = {};
|
||||||
|
isContentScript: boolean = false;
|
||||||
|
isBackgroundScript: boolean = true;
|
||||||
|
vuexStore: any;
|
||||||
|
uwInstance: any;
|
||||||
|
conf: any;
|
||||||
|
startTime: number;
|
||||||
|
stopTime: number;
|
||||||
|
|
||||||
|
constructor(options?: {vuexStore?: any, uwInstance?: any}) {
|
||||||
this.vuexStore = options?.vuexStore;
|
this.vuexStore = options?.vuexStore;
|
||||||
this.uwInstance = options?.uwInstance;
|
this.uwInstance = options?.uwInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
static saveConfig(conf) {
|
static saveConfig(conf: LoggerConfig) {
|
||||||
if (process.env.CHANNEL === 'dev') {
|
if (process.env.CHANNEL === 'dev') {
|
||||||
console.info('Saving logger conf:', conf)
|
console.info('Saving logger conf:', conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentBrowser.firefox || currentBrowser.edge) {
|
browser.storage.local.set( {'uwLogger': JSON.stringify(conf)});
|
||||||
return browser.storage.local.set( {'uwLogger': JSON.stringify(conf)});
|
|
||||||
} else if (currentBrowser.chrome) {
|
|
||||||
return chrome.storage.local.set( {'uwLogger': JSON.stringify(conf)});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static syncConfig(callback) {
|
static syncConfig(callback: (x) => void) {
|
||||||
const br = currentBrowser.firefox ? browser : chrome;
|
browser.storage.onChanged.addListener( (changes, area) => {
|
||||||
br.storage.onChanged.addListener( (changes, area) => {
|
|
||||||
if (changes.uwLogger) {
|
if (changes.uwLogger) {
|
||||||
const newLoggerConf = JSON.parse(changes.uwLogger.newValue)
|
const newLoggerConf = JSON.parse(changes.uwLogger.newValue)
|
||||||
if (process.env.CHANNEL === 'dev') {
|
if (process.env.CHANNEL === 'dev') {
|
||||||
console.info('Logger settings reloaded. New conf:', conf);
|
console.info('Logger settings reloaded. New conf:', newLoggerConf);
|
||||||
}
|
}
|
||||||
callback(newLoggerConf);
|
callback(newLoggerConf);
|
||||||
}
|
}
|
||||||
@ -46,17 +116,13 @@ class Logger {
|
|||||||
static async getConfig() {
|
static async getConfig() {
|
||||||
let ret;
|
let ret;
|
||||||
|
|
||||||
if (currentBrowser.firefox) {
|
// if (BrowserDetect.firefox) {
|
||||||
ret = await browser.storage.local.get('uwLogger');
|
ret = await browser.storage.local.get('uwLogger');
|
||||||
} else if (currentBrowser.chrome) {
|
// } else if (BrowserDetect.anyChromium) {
|
||||||
ret = await new Promise( (resolve, reject) => {
|
// ret = await new Promise( (resolve, reject) => {
|
||||||
chrome.storage.local.get('uwLogger', (res) => resolve(res));
|
// browser.storage.local.get('uwLogger', (res) => resolve(res));
|
||||||
});
|
// });
|
||||||
} else if (currentBrowser.edge) {
|
// }
|
||||||
ret = await new Promise( (resolve, reject) => {
|
|
||||||
browser.storage.local.get('uwLogger', (res) => resolve(res));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.CHANNEL === 'dev') {
|
if (process.env.CHANNEL === 'dev') {
|
||||||
try {
|
try {
|
||||||
@ -73,7 +139,7 @@ class Logger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async init(conf) {
|
async init(conf: LoggerConfig) {
|
||||||
// this is the only property that always gets passed via conf
|
// this is the only property that always gets passed via conf
|
||||||
// and doesn't get ignored even if the rest of the conf gets
|
// and doesn't get ignored even if the rest of the conf gets
|
||||||
// loaded from browser storage
|
// loaded from browser storage
|
||||||
@ -101,9 +167,7 @@ class Logger {
|
|||||||
this.temp_disable = false;
|
this.temp_disable = false;
|
||||||
this.stopTime = this.conf.timeout ? performance.now() + (this.conf.timeout * 1000) : undefined;
|
this.stopTime = this.conf.timeout ? performance.now() + (this.conf.timeout * 1000) : undefined;
|
||||||
|
|
||||||
const br = currentBrowser.firefox ? browser : chrome;
|
browser.storage.onChanged.addListener( (changes, area) => {
|
||||||
|
|
||||||
br.storage.onChanged.addListener( (changes, area) => {
|
|
||||||
if (process.env.CHANNEL === 'dev') {
|
if (process.env.CHANNEL === 'dev') {
|
||||||
if (!changes.uwLogger) {
|
if (!changes.uwLogger) {
|
||||||
// console.info('[Logger::<storage/on change> No new logger settings!');
|
// console.info('[Logger::<storage/on change> No new logger settings!');
|
||||||
@ -131,7 +195,7 @@ class Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.log = [];
|
this.history = [];
|
||||||
this.startTime = performance.now();
|
this.startTime = performance.now();
|
||||||
this.stopTime = this.conf.timeout ? performance.now() + (this.conf.timeout * 1000) : undefined;
|
this.stopTime = this.conf.timeout ? performance.now() + (this.conf.timeout * 1000) : undefined;
|
||||||
}
|
}
|
||||||
@ -142,9 +206,9 @@ class Logger {
|
|||||||
Logger.saveConfig(conf);
|
Logger.saveConfig(conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSaved() {
|
// async getSaved() {
|
||||||
return Logger.getSaved();
|
// return Logger.getSaved();
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
// allow syncing of start times between bg and page scripts.
|
// allow syncing of start times between bg and page scripts.
|
||||||
@ -174,7 +238,7 @@ class Logger {
|
|||||||
getFileLogJSONString() {
|
getFileLogJSONString() {
|
||||||
return {
|
return {
|
||||||
site: window && window.location,
|
site: window && window.location,
|
||||||
log: JSON.toString(this.history),
|
log: JSON.stringify(this.history),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,7 +277,7 @@ class Logger {
|
|||||||
parseStack() {
|
parseStack() {
|
||||||
const trace = (new Error()).stack;
|
const trace = (new Error()).stack;
|
||||||
|
|
||||||
const stackInfo = {};
|
const stackInfo: any = {};
|
||||||
// we turn our stack into array and remove the "file::line" part of the trace,
|
// we turn our stack into array and remove the "file::line" part of the trace,
|
||||||
// since that is useless because minification/webpack
|
// since that is useless because minification/webpack
|
||||||
stackInfo['stack'] = {trace: trace.split('\n').map(a => a.split('@')[0])};
|
stackInfo['stack'] = {trace: trace.split('\n').map(a => a.split('@')[0])};
|
||||||
@ -336,7 +400,7 @@ class Logger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canLogConsole(component, stackInfo) {
|
canLogConsole(component, stackInfo?) {
|
||||||
if (!this.conf.consoleOptions?.enabled || this.temp_disable) {
|
if (!this.conf.consoleOptions?.enabled || this.temp_disable) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -437,7 +501,7 @@ class Logger {
|
|||||||
let ts = performance.now();
|
let ts = performance.now();
|
||||||
let secondMark = ts - 1000;
|
let secondMark = ts - 1000;
|
||||||
let halfSecondMark = ts - 500;
|
let halfSecondMark = ts - 500;
|
||||||
let i = this.history.length();
|
let i = this.history.length;
|
||||||
|
|
||||||
// correct ts _after_ secondMark and halfSecondMark were determined
|
// correct ts _after_ secondMark and halfSecondMark were determined
|
||||||
if (ts <= this.history[this.history.length - 1]) {
|
if (ts <= this.history[this.history.length - 1]) {
|
@ -4,7 +4,7 @@ class ObjectCopy {
|
|||||||
static addNew(current, newValues){
|
static addNew(current, newValues){
|
||||||
|
|
||||||
// clone target
|
// clone target
|
||||||
var out = JSON.parse(JSON.stringify(newValues));
|
let out = JSON.parse(JSON.stringify(newValues));
|
||||||
|
|
||||||
if(! current) {
|
if(! current) {
|
||||||
if(Debug.debug) {
|
if(Debug.debug) {
|
||||||
@ -14,7 +14,7 @@ class ObjectCopy {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(var k in out) {
|
for(let k in out) {
|
||||||
// if current key exist, replace it with existing value. Take no action otherwise.
|
// if current key exist, replace it with existing value. Take no action otherwise.
|
||||||
if(current[k]) {
|
if(current[k]) {
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ class ObjectCopy {
|
|||||||
|
|
||||||
// add the values that would otherwise be deleted back to our object. (We need that so user-defined
|
// add the values that would otherwise be deleted back to our object. (We need that so user-defined
|
||||||
// sites don't get forgotten)
|
// sites don't get forgotten)
|
||||||
for(var k in current) {
|
for(let k in current) {
|
||||||
if (! out[k]) {
|
if (! out[k]) {
|
||||||
out[k] = current[k];
|
out[k] = current[k];
|
||||||
}
|
}
|
||||||
@ -47,7 +47,7 @@ class ObjectCopy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static overwrite(current, newValues){
|
static overwrite(current, newValues){
|
||||||
for(var k in newValues) {
|
for(let k in newValues) {
|
||||||
// if current key exist, replace it with existing value. Take no action otherwise.
|
// if current key exist, replace it with existing value. Take no action otherwise.
|
||||||
if (current[k] !== undefined) {
|
if (current[k] !== undefined) {
|
||||||
// Types and constructors of objects must match. If they don't, we always use the new value.
|
// Types and constructors of objects must match. If they don't, we always use the new value.
|
@ -1,19 +1,41 @@
|
|||||||
import Debug from '../conf/Debug';
|
import Debug from '../conf/Debug';
|
||||||
import currentBrowser from '../conf/BrowserDetect';
|
import currentBrowser from '../conf/BrowserDetect';
|
||||||
import ExtensionConf from '../conf/ExtensionConf';
|
import ExtensionConf from '../conf/ExtensionConf';
|
||||||
import ExtensionMode from '../../common/enums/extension-mode.enum';
|
import ExtensionMode from '../../common/enums/ExtensionMode.enum';
|
||||||
import ObjectCopy from '../lib/ObjectCopy';
|
import ObjectCopy from './ObjectCopy';
|
||||||
import Stretch from '../../common/enums/stretch.enum';
|
import StretchType from '../../common/enums/StretchType.enum';
|
||||||
import VideoAlignment from '../../common/enums/video-alignment.enum';
|
import VideoAlignmentType from '../../common/enums/VideoAlignmentType.enum';
|
||||||
import ExtensionConfPatch from '../conf/ExtConfPatches';
|
import ExtensionConfPatch from '../conf/ExtConfPatches';
|
||||||
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
|
import CropModePersistence from '../../common/enums/CropModePersistence.enum';
|
||||||
import BrowserDetect from '../conf/BrowserDetect';
|
import BrowserDetect from '../conf/BrowserDetect';
|
||||||
|
import Logger from './Logger';
|
||||||
|
import SettingsInterface from '../../common/interfaces/SettingsInterface';
|
||||||
|
import { browser } from 'webextension-polyfill-ts';
|
||||||
|
|
||||||
if(process.env.CHANNEL !== 'stable'){
|
if(process.env.CHANNEL !== 'stable'){
|
||||||
console.info("Loading Settings");
|
console.info("Loading Settings");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Settings {
|
class Settings {
|
||||||
|
//#region flags
|
||||||
|
useSync: boolean = false;
|
||||||
|
version: string;
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region helper classes
|
||||||
|
logger: Logger;
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region data
|
||||||
|
default: SettingsInterface; // default settings
|
||||||
|
active: SettingsInterface; // currently active settings
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region callbacks
|
||||||
|
onSettingsChanged: any;
|
||||||
|
afterSettingsSaved: any;
|
||||||
|
//#endregion
|
||||||
|
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
// Options: activeSettings, updateCallback, logger
|
// Options: activeSettings, updateCallback, logger
|
||||||
@ -23,14 +45,8 @@ class Settings {
|
|||||||
this.active = options?.activeSettings ?? undefined;
|
this.active = options?.activeSettings ?? undefined;
|
||||||
this.default = ExtensionConf;
|
this.default = ExtensionConf;
|
||||||
this.default['version'] = this.getExtensionVersion();
|
this.default['version'] = this.getExtensionVersion();
|
||||||
this.useSync = false;
|
|
||||||
this.version = undefined;
|
|
||||||
|
|
||||||
if (currentBrowser.firefox) {
|
browser.storage.onChanged.addListener((changes, area) => {this.storageChangeListener(changes, area)});
|
||||||
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) {
|
storageChangeListener(changes, area) {
|
||||||
@ -60,14 +76,10 @@ class Settings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static getExtensionVersion() {
|
static getExtensionVersion(): string {
|
||||||
if (currentBrowser.firefox) {
|
return browser.runtime.getManifest().version;
|
||||||
return browser.runtime.getManifest().version;
|
|
||||||
} else if (currentBrowser.chrome) {
|
|
||||||
return chrome.runtime.getManifest().version;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
getExtensionVersion() {
|
getExtensionVersion(): string {
|
||||||
return Settings.getExtensionVersion();
|
return Settings.getExtensionVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +108,10 @@ class Settings {
|
|||||||
// also, the fourth digit can start with a letter.
|
// also, the fourth digit can start with a letter.
|
||||||
// versions that start with a letter are ranked lower than
|
// versions that start with a letter are ranked lower than
|
||||||
// versions x.x.x.0
|
// versions x.x.x.0
|
||||||
if (isNaN(+aa[3]) ^ isNaN(+bb[3])) {
|
if (
|
||||||
|
(isNaN(+aa[3]) && !isNaN(+bb[3]))
|
||||||
|
|| (!isNaN(+aa[3]) && isNaN(+bb[3]))
|
||||||
|
) {
|
||||||
return isNaN(+aa[3]) ? -1 : 1;
|
return isNaN(+aa[3]) ? -1 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,17 +283,7 @@ class Settings {
|
|||||||
async get() {
|
async get() {
|
||||||
let ret;
|
let ret;
|
||||||
|
|
||||||
if (currentBrowser.firefox) {
|
ret = await browser.storage.local.get('uwSettings');
|
||||||
ret = await browser.storage.local.get('uwSettings');
|
|
||||||
} else if (currentBrowser.chrome) {
|
|
||||||
ret = await new Promise( (resolve, reject) => {
|
|
||||||
chrome.storage.local.get('uwSettings', (res) => resolve(res));
|
|
||||||
});
|
|
||||||
} else if (currentBrowser.edge) {
|
|
||||||
ret = await new Promise( (resolve, reject) => {
|
|
||||||
browser.storage.local.get('uwSettings', (res) => resolve(res));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger?.log('info', 'settings', 'Got settings:', ret && ret.uwSettings && JSON.parse(ret.uwSettings));
|
this.logger?.log('info', 'settings', 'Got settings:', ret && ret.uwSettings && JSON.parse(ret.uwSettings));
|
||||||
|
|
||||||
@ -301,10 +306,10 @@ class Settings {
|
|||||||
sites[site].autoar = ExtensionMode.Default;
|
sites[site].autoar = ExtensionMode.Default;
|
||||||
}
|
}
|
||||||
if (sites[site].stretch === undefined) {
|
if (sites[site].stretch === undefined) {
|
||||||
sites[site].stretch = Stretch.Default;
|
sites[site].stretch = StretchType.Default;
|
||||||
}
|
}
|
||||||
if (sites[site].videoAlignment === undefined) {
|
if (sites[site].videoAlignment === undefined) {
|
||||||
sites[site].videoAlignment = VideoAlignment.Default;
|
sites[site].videoAlignment = VideoAlignmentType.Default;
|
||||||
}
|
}
|
||||||
if (sites[site].keyboardShortcutsEnabled === undefined) {
|
if (sites[site].keyboardShortcutsEnabled === undefined) {
|
||||||
sites[site].keyboardShortcutsEnabled = ExtensionMode.Default;
|
sites[site].keyboardShortcutsEnabled = ExtensionMode.Default;
|
||||||
@ -312,7 +317,7 @@ class Settings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async set(extensionConf, options) {
|
async set(extensionConf, options?) {
|
||||||
if (!options || !options.forcePreserveVersion) {
|
if (!options || !options.forcePreserveVersion) {
|
||||||
extensionConf.version = this.version;
|
extensionConf.version = this.version;
|
||||||
}
|
}
|
||||||
@ -321,11 +326,7 @@ class Settings {
|
|||||||
|
|
||||||
this.logger?.log('info', 'settings', "[Settings::set] setting new settings:", extensionConf)
|
this.logger?.log('info', 'settings', "[Settings::set] setting new settings:", extensionConf)
|
||||||
|
|
||||||
if (BrowserDetect.firefox) {
|
return browser.storage.local.set( {'uwSettings': JSON.stringify(extensionConf)});
|
||||||
return browser.storage.local.set( {'uwSettings': JSON.stringify(extensionConf)});
|
|
||||||
} else {
|
|
||||||
return chrome.storage.local.set( {'uwSettings': JSON.stringify(extensionConf)});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async setActive(activeSettings) {
|
async setActive(activeSettings) {
|
||||||
@ -336,7 +337,7 @@ class Settings {
|
|||||||
this.active[prop] = value;
|
this.active[prop] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(options) {
|
async save(options?) {
|
||||||
if (Debug.debug && Debug.storage) {
|
if (Debug.debug && Debug.storage) {
|
||||||
console.log("[Settings::save] Saving active settings:", this.active);
|
console.log("[Settings::save] Saving active settings:", this.active);
|
||||||
}
|
}
|
||||||
@ -389,7 +390,7 @@ class Settings {
|
|||||||
return this.active.actions;
|
return this.active.actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
getExtensionMode(site) {
|
getExtensionMode(site?: string) {
|
||||||
if (!site) {
|
if (!site) {
|
||||||
site = window.location.hostname;
|
site = window.location.hostname;
|
||||||
|
|
||||||
@ -494,7 +495,7 @@ class Settings {
|
|||||||
return this.canStartExtension(site);
|
return this.canStartExtension(site);
|
||||||
}
|
}
|
||||||
|
|
||||||
canStartAutoAr(site) {
|
canStartAutoAr(site?: string) {
|
||||||
// 'site' argument is only ever used when calling this function recursively for debugging
|
// 'site' argument is only ever used when calling this function recursively for debugging
|
||||||
if (!site) {
|
if (!site) {
|
||||||
site = window.location.hostname;
|
site = window.location.hostname;
|
||||||
@ -539,13 +540,13 @@ class Settings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultOption(option) {
|
getDefaultOption(option?) {
|
||||||
const allDefault = {
|
const allDefault = {
|
||||||
mode: ExtensionMode.Default,
|
mode: ExtensionMode.Default,
|
||||||
autoar: ExtensionMode.Default,
|
autoar: ExtensionMode.Default,
|
||||||
autoarFallback: ExtensionMode.Default,
|
autoarFallback: ExtensionMode.Default,
|
||||||
stretch: Stretch.Default,
|
stretch: StretchType.Default,
|
||||||
videoAlignment: VideoAlignment.Default,
|
videoAlignment: VideoAlignmentType.Default,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!option || allDefault[option] === undefined) {
|
if (!option || allDefault[option] === undefined) {
|
||||||
@ -565,7 +566,7 @@ class Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDefaultStretchMode(site) {
|
getDefaultStretchMode(site) {
|
||||||
if (site && (this.active.sites[site]?.stretch ?? Stretch.Default) !== Stretch.Default) {
|
if (site && (this.active.sites[site]?.stretch ?? StretchType.Default) !== StretchType.Default) {
|
||||||
return this.active.sites[site].stretch;
|
return this.active.sites[site].stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,7 +574,7 @@ class Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDefaultCropPersistenceMode(site) {
|
getDefaultCropPersistenceMode(site) {
|
||||||
if (site && (this.active.sites[site]?.cropModePersistence ?? Stretch.Default) !== Stretch.Default) {
|
if (site && (this.active.sites[site]?.cropModePersistence ?? StretchType.Default) !== StretchType.Default) {
|
||||||
return this.active.sites[site].cropModePersistence;
|
return this.active.sites[site].cropModePersistence;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -582,7 +583,7 @@ class Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDefaultVideoAlignment(site) {
|
getDefaultVideoAlignment(site) {
|
||||||
if ( (this.active.sites[site]?.videoAlignment ?? VideoAlignment.Default) !== VideoAlignment.Default) {
|
if ( (this.active.sites[site]?.videoAlignment ?? VideoAlignmentType.Default) !== VideoAlignmentType.Default) {
|
||||||
return this.active.sites[site].videoAlignment;
|
return this.active.sites[site].videoAlignment;
|
||||||
}
|
}
|
||||||
|
|
@ -1,3 +1,3 @@
|
|||||||
export async function sleep(timeout) {
|
export async function sleep(timeout) {
|
||||||
return new Promise( (resolve, reject) => setTimeout(() => resolve(), timeout));
|
return new Promise( (resolve, reject) => setTimeout(() => resolve(null), timeout));
|
||||||
}
|
}
|
@ -6,12 +6,56 @@ import EdgeDetectPrimaryDirection from './edge-detect/enums/EdgeDetectPrimaryDir
|
|||||||
import EdgeDetectQuality from './edge-detect/enums/EdgeDetectQualityEnum';
|
import EdgeDetectQuality from './edge-detect/enums/EdgeDetectQualityEnum';
|
||||||
import GuardLine from './GuardLine';
|
import GuardLine from './GuardLine';
|
||||||
// import DebugCanvas from './DebugCanvas';
|
// import DebugCanvas from './DebugCanvas';
|
||||||
import VideoAlignment from '../../../common/enums/video-alignment.enum';
|
import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum';
|
||||||
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
|
import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
|
||||||
import {sleep} from '../../lib/Util';
|
import {sleep} from '../Util';
|
||||||
import BrowserDetect from '../../conf/BrowserDetect';
|
import BrowserDetect from '../../conf/BrowserDetect';
|
||||||
|
import Logger from '../Logger';
|
||||||
|
import VideoData from '../video-data/VideoData';
|
||||||
|
import Settings from '../Settings';
|
||||||
|
|
||||||
class ArDetector {
|
class ArDetector {
|
||||||
|
logger: Logger;
|
||||||
|
conf: VideoData;
|
||||||
|
video: HTMLVideoElement;
|
||||||
|
settings: Settings;
|
||||||
|
|
||||||
|
guardLine: GuardLine;
|
||||||
|
edgeDetector: EdgeDetect;
|
||||||
|
|
||||||
|
setupTimer: any;
|
||||||
|
sampleCols: any[];
|
||||||
|
sampleLines
|
||||||
|
|
||||||
|
canFallback: boolean = true;
|
||||||
|
fallbackMode: boolean = false;
|
||||||
|
|
||||||
|
blackLevel: number;
|
||||||
|
|
||||||
|
arid: string;
|
||||||
|
|
||||||
|
// ar detector starts in this state. running main() sets both to false
|
||||||
|
_paused: boolean;
|
||||||
|
_halted: boolean = true;
|
||||||
|
_exited: boolean = true;
|
||||||
|
|
||||||
|
private manualTickEnabled: boolean;
|
||||||
|
_nextTick: boolean;
|
||||||
|
canDoFallbackMode: boolean = false;
|
||||||
|
|
||||||
|
// helper objects
|
||||||
|
private attachedCanvas: HTMLCanvasElement;
|
||||||
|
canvas: HTMLCanvasElement;
|
||||||
|
private blackframeCanvas: HTMLCanvasElement;
|
||||||
|
private context: CanvasRenderingContext2D;
|
||||||
|
private blackframeContext: CanvasRenderingContext2D;
|
||||||
|
private canvasScaleFactor: number;
|
||||||
|
private detectionTimeoutEventCount: number;
|
||||||
|
canvasImageDataRowLength: number;
|
||||||
|
private noLetterboxCanvasReset: boolean;
|
||||||
|
private detectedAr: any;
|
||||||
|
private canvasDrawWindowHOffset: number;
|
||||||
|
private sampleCols_current: number;
|
||||||
|
|
||||||
constructor(videoData){
|
constructor(videoData){
|
||||||
this.logger = videoData.logger;
|
this.logger = videoData.logger;
|
||||||
@ -19,34 +63,18 @@ class ArDetector {
|
|||||||
this.video = videoData.video;
|
this.video = videoData.video;
|
||||||
this.settings = videoData.settings;
|
this.settings = videoData.settings;
|
||||||
|
|
||||||
this.setupTimer = null;
|
|
||||||
|
|
||||||
this.sampleCols = [];
|
this.sampleCols = [];
|
||||||
|
|
||||||
this.canFallback = true;
|
|
||||||
this.fallbackMode = false;
|
|
||||||
|
|
||||||
this.blackLevel = this.settings.active.arDetect.blackbar.blackLevel;
|
this.blackLevel = this.settings.active.arDetect.blackbar.blackLevel;
|
||||||
|
|
||||||
this.arid = (Math.random()*100).toFixed();
|
this.arid = (Math.random()*100).toFixed();
|
||||||
|
|
||||||
// ar detector starts in this state. running main() sets both to false
|
|
||||||
this._halted = true;
|
|
||||||
this._exited = true;
|
|
||||||
|
|
||||||
// we can tick manually, for debugging
|
// we can tick manually, for debugging
|
||||||
this._manualTicks = false;
|
|
||||||
this._nextTick = false;
|
|
||||||
|
|
||||||
this.canDoFallbackMode = false;
|
|
||||||
|
|
||||||
this.drmNotificationShown = false;
|
|
||||||
|
|
||||||
this.logger.log('info', 'init', `[ArDetector::ctor] creating new ArDetector. arid: ${this.arid}`);
|
this.logger.log('info', 'init', `[ArDetector::ctor] creating new ArDetector. arid: ${this.arid}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
setManualTick(manualTick) {
|
setManualTick(manualTick) {
|
||||||
this._manualTicks = manualTick;
|
this.manualTickEnabled = manualTick;
|
||||||
}
|
}
|
||||||
|
|
||||||
tick() {
|
tick() {
|
||||||
@ -68,19 +96,19 @@ class ArDetector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy(){
|
destroy(){
|
||||||
this.logger.log('info', 'init', `%c[ArDetect::destroy] <@${this.arid}> Destroying aard.`, _ard_console_stop, e);
|
this.logger.log('info', 'init', `%c[ArDetect::destroy] <@${this.arid}> Destroying aard.`, _ard_console_stop);
|
||||||
// this.debugCanvas.destroy();
|
// this.debugCanvas.destroy();
|
||||||
this.stop();
|
this.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
setup(cwidth, cheight){
|
setup(cwidth?: number, cheight?: number){
|
||||||
this.logger.log('info', 'init', `[ArDetect::setup] <@${this.arid}> Starting autodetection setup.`);
|
this.logger.log('info', 'init', `[ArDetect::setup] <@${this.arid}> Starting autodetection setup.`);
|
||||||
//
|
//
|
||||||
// [-1] check for zero-width and zero-height videos. If we detect this, we kick the proverbial
|
// [-1] check for zero-width and zero-height videos. If we detect this, we kick the proverbial
|
||||||
// can some distance down the road. This problem will prolly fix itself soon. We'll also
|
// can some distance down the road. This problem will prolly fix itself soon. We'll also
|
||||||
// not do any other setup until this issue is fixed
|
// not do any other setup until this issue is fixed
|
||||||
//
|
//
|
||||||
if(this.video.videoWidth === 0 || this.video.videoHeight === 0 ){
|
if (this.video.videoWidth === 0 || this.video.videoHeight === 0 ){
|
||||||
this.logger.log('warn', 'debug', `[ArDetect::setup] <@${this.arid}> This video has zero width or zero height. Dimensions: ${this.video.videoWidth} × ${this.video.videoHeight}`);
|
this.logger.log('warn', 'debug', `[ArDetect::setup] <@${this.arid}> This video has zero width or zero height. Dimensions: ${this.video.videoWidth} × ${this.video.videoHeight}`);
|
||||||
|
|
||||||
this.scheduleInitRestart();
|
this.scheduleInitRestart();
|
||||||
@ -133,16 +161,16 @@ class ArDetector {
|
|||||||
// [2] determine places we'll use to sample our main frame
|
// [2] determine places we'll use to sample our main frame
|
||||||
//
|
//
|
||||||
|
|
||||||
var ncol = this.settings.active.arDetect.sampling.staticCols;
|
let ncol = this.settings.active.arDetect.sampling.staticCols;
|
||||||
var nrow = this.settings.active.arDetect.sampling.staticRows;
|
let nrow = this.settings.active.arDetect.sampling.staticRows;
|
||||||
|
|
||||||
var colSpacing = this.canvas.width / ncol;
|
let colSpacing = this.canvas.width / ncol;
|
||||||
var rowSpacing = (this.canvas.height << 2) / nrow;
|
let rowSpacing = (this.canvas.height << 2) / nrow;
|
||||||
|
|
||||||
this.sampleLines = [];
|
this.sampleLines = [];
|
||||||
this.sampleCols = [];
|
this.sampleCols = [];
|
||||||
|
|
||||||
for(var i = 0; i < ncol; i++){
|
for(let i = 0; i < ncol; i++){
|
||||||
if(i < ncol - 1)
|
if(i < ncol - 1)
|
||||||
this.sampleCols.push(Math.round(colSpacing * i));
|
this.sampleCols.push(Math.round(colSpacing * i));
|
||||||
else{
|
else{
|
||||||
@ -150,7 +178,7 @@ class ArDetector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(var i = 0; i < nrow; i++){
|
for(let i = 0; i < nrow; i++){
|
||||||
if(i < ncol - 5)
|
if(i < ncol - 5)
|
||||||
this.sampleLines.push(Math.round(rowSpacing * i));
|
this.sampleLines.push(Math.round(rowSpacing * i));
|
||||||
else{
|
else{
|
||||||
@ -173,7 +201,7 @@ class ArDetector {
|
|||||||
//
|
//
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.blackframeContext.drawWindow(window,0, 0, this.blackframeCanvas.width, this.blackframeCanvas.height, "rgba(0,0,128,1)");
|
(this.blackframeContext as any).drawWindow(window,0, 0, this.blackframeCanvas.width, this.blackframeCanvas.height, "rgba(0,0,128,1)");
|
||||||
this.canDoFallbackMode = true;
|
this.canDoFallbackMode = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.canDoFallbackMode = false;
|
this.canDoFallbackMode = false;
|
||||||
@ -187,7 +215,7 @@ class ArDetector {
|
|||||||
this.resetBlackLevel();
|
this.resetBlackLevel();
|
||||||
|
|
||||||
// if we're restarting ArDetect, we need to do this in order to force-recalculate aspect ratio
|
// if we're restarting ArDetect, we need to do this in order to force-recalculate aspect ratio
|
||||||
this.conf.resizer.setLastAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
|
this.conf.resizer.setLastAr({type: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
|
||||||
|
|
||||||
this.canvasImageDataRowLength = cwidth << 2;
|
this.canvasImageDataRowLength = cwidth << 2;
|
||||||
this.noLetterboxCanvasReset = false;
|
this.noLetterboxCanvasReset = false;
|
||||||
@ -212,9 +240,9 @@ class ArDetector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.conf.resizer.lastAr.type === AspectRatio.Automatic) {
|
if (this.conf.resizer.lastAr.type === AspectRatioType.Automatic) {
|
||||||
// ensure first autodetection will run in any case
|
// ensure first autodetection will run in any case
|
||||||
this.conf.resizer.setLastAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
|
this.conf.resizer.setLastAr({type: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -292,7 +320,7 @@ class ArDetector {
|
|||||||
// state from 'paused' to 'playing', we don't need to wait for the rest of the longer
|
// state from 'paused' to 'playing', we don't need to wait for the rest of the longer
|
||||||
// paused state timeout to finish.
|
// paused state timeout to finish.
|
||||||
|
|
||||||
if ( (!this._manualTicks && this.canTriggerFrameCheck(lastFrameCheckStartTime)) || this._nextTick) {
|
if ( (!this.manualTickEnabled && this.canTriggerFrameCheck(lastFrameCheckStartTime)) || this._nextTick) {
|
||||||
this._nextTick = false;
|
this._nextTick = false;
|
||||||
|
|
||||||
lastFrameCheckStartTime = Date.now();
|
lastFrameCheckStartTime = Date.now();
|
||||||
@ -344,7 +372,7 @@ class ArDetector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
scheduleInitRestart(timeout, force_reset){
|
scheduleInitRestart(timeout?: number, force_reset?: boolean){
|
||||||
if(! timeout){
|
if(! timeout){
|
||||||
timeout = 100;
|
timeout = 100;
|
||||||
}
|
}
|
||||||
@ -353,7 +381,7 @@ class ArDetector {
|
|||||||
clearTimeout(this.setupTimer);
|
clearTimeout(this.setupTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
var ths = this;
|
let ths = this;
|
||||||
this.setupTimer = setTimeout(function(){
|
this.setupTimer = setTimeout(function(){
|
||||||
ths.setupTimer = null;
|
ths.setupTimer = null;
|
||||||
try{
|
try{
|
||||||
@ -391,7 +419,7 @@ class ArDetector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTimeout(baseTimeout, startTime){
|
getTimeout(baseTimeout, startTime){
|
||||||
var execTime = (performance.now() - startTime);
|
let execTime = (performance.now() - startTime);
|
||||||
|
|
||||||
return baseTimeout;
|
return baseTimeout;
|
||||||
}
|
}
|
||||||
@ -447,12 +475,12 @@ class ArDetector {
|
|||||||
// letterbox also needs to be corrected:
|
// letterbox also needs to be corrected:
|
||||||
// letterbox += [video.zoomedHeight] - [video.unzoomedHeight]
|
// letterbox += [video.zoomedHeight] - [video.unzoomedHeight]
|
||||||
|
|
||||||
var vbr = this.video.getBoundingClientRect();
|
let vbr = this.video.getBoundingClientRect();
|
||||||
|
|
||||||
zoomFactor = vbr.height / this.video.clientHeight;
|
zoomFactor = vbr.height / this.video.clientHeight;
|
||||||
letterbox += vbr.height - this.video.clientHeight;
|
letterbox += vbr.height - this.video.clientHeight;
|
||||||
|
|
||||||
var trueHeight = this.canvas.height * zoomFactor - letterbox;
|
let trueHeight = this.canvas.height * zoomFactor - letterbox;
|
||||||
|
|
||||||
if(edges.top > 1 && edges.top <= this.settings.active.arDetect.fallbackMode.noTriggerZonePx ){
|
if(edges.top > 1 && edges.top <= this.settings.active.arDetect.fallbackMode.noTriggerZonePx ){
|
||||||
this.logger.log('info', 'arDetect', `%c[ArDetect::calculateArFromEdges] <@${this.arid}> Edge is in the no-trigger zone. Aspect ratio change is not triggered.`)
|
this.logger.log('info', 'arDetect', `%c[ArDetect::calculateArFromEdges] <@${this.arid}> Edge is in the no-trigger zone. Aspect ratio change is not triggered.`)
|
||||||
@ -475,13 +503,13 @@ class ArDetector {
|
|||||||
// poglejmo, če se je razmerje stranic spremenilo
|
// poglejmo, če se je razmerje stranic spremenilo
|
||||||
// check if aspect ratio is changed:
|
// check if aspect ratio is changed:
|
||||||
let lastAr = this.conf.resizer.getLastAr();
|
let lastAr = this.conf.resizer.getLastAr();
|
||||||
if (lastAr.type === AspectRatio.Automatic && lastAr.ratio !== null && lastAr.ratio !== undefined){
|
if (lastAr.type === AspectRatioType.Automatic && lastAr.ratio !== null && lastAr.ratio !== undefined){
|
||||||
// spremembo lahko zavrnemo samo, če uporabljamo avtomatski način delovanja in če smo razmerje stranic
|
// spremembo lahko zavrnemo samo, če uporabljamo avtomatski način delovanja in če smo razmerje stranic
|
||||||
// že nastavili.
|
// že nastavili.
|
||||||
//
|
//
|
||||||
// we can only deny aspect ratio changes if we use automatic mode and if aspect ratio was set from here.
|
// we can only deny aspect ratio changes if we use automatic mode and if aspect ratio was set from here.
|
||||||
|
|
||||||
let arDiff = trueAr - lastAr.ar;
|
let arDiff = trueAr - lastAr.ratio;
|
||||||
|
|
||||||
if (arDiff < 0)
|
if (arDiff < 0)
|
||||||
arDiff = -arDiff;
|
arDiff = -arDiff;
|
||||||
@ -490,7 +518,7 @@ class ArDetector {
|
|||||||
|
|
||||||
// ali je sprememba v mejah dovoljenega? Če da -> fertik
|
// ali je sprememba v mejah dovoljenega? Če da -> fertik
|
||||||
// is ar variance within acceptable levels? If yes -> we done
|
// is ar variance within acceptable levels? If yes -> we done
|
||||||
this.logger.log('info', 'arDetect', `%c[ArDetect::processAr] <@${this.arid}> New aspect ratio varies from the old one by this much:\n`,"color: #aaf","old Ar", lastAr.ar, "current ar", trueAr, "arDiff (absolute):",arDiff,"ar diff (relative to new ar)", arDiff_percent);
|
this.logger.log('info', 'arDetect', `%c[ArDetect::processAr] <@${this.arid}> New aspect ratio varies from the old one by this much:\n`,"color: #aaf","old Ar", lastAr.ratio, "current ar", trueAr, "arDiff (absolute):",arDiff,"ar diff (relative to new ar)", arDiff_percent);
|
||||||
|
|
||||||
if (arDiff < trueAr * this.settings.active.arDetect.allowedArVariance){
|
if (arDiff < trueAr * this.settings.active.arDetect.allowedArVariance){
|
||||||
this.logger.log('info', 'arDetect', `%c[ArDetect::processAr] <@${this.arid}> Aspect ratio change denied — diff %: ${arDiff_percent}`, "background: #740; color: #fa2");
|
this.logger.log('info', 'arDetect', `%c[ArDetect::processAr] <@${this.arid}> Aspect ratio change denied — diff %: ${arDiff_percent}`, "background: #740; color: #fa2");
|
||||||
@ -500,42 +528,16 @@ class ArDetector {
|
|||||||
}
|
}
|
||||||
this.logger.log('info', 'debug', `%c[ArDetect::processAr] <@${this.arid}> Triggering aspect ratio change. New aspect ratio: ${trueAr}`, _ard_console_change);
|
this.logger.log('info', 'debug', `%c[ArDetect::processAr] <@${this.arid}> Triggering aspect ratio change. New aspect ratio: ${trueAr}`, _ard_console_change);
|
||||||
|
|
||||||
this.conf.resizer.updateAr({type: AspectRatio.Automatic, ratio: trueAr}, {type: AspectRatio.Automatic, ratio: trueAr});
|
this.conf.resizer.updateAr({type: AspectRatioType.Automatic, ratio: trueAr});
|
||||||
}
|
}
|
||||||
|
|
||||||
clearImageData(id) {
|
clearImageData(id) {
|
||||||
if (ArrayBuffer.transfer) {
|
if ((ArrayBuffer as any).transfer) {
|
||||||
ArrayBuffer.transfer(id, 0);
|
(ArrayBuffer as any).transfer(id, 0);
|
||||||
}
|
}
|
||||||
id = undefined;
|
id = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether video we're trying to play is protected by DRM.
|
|
||||||
*
|
|
||||||
* When trying to get an image frame of a DRM-protected video in
|
|
||||||
* firefox, the method canvas.drawImage(video) will throw an exception.
|
|
||||||
*
|
|
||||||
* This doesn't happen in Chrome. As opposed to Firefox, chrome will
|
|
||||||
* simply draw a transparent black image and not tell anyone that
|
|
||||||
* anything is amiss. However, since the image is (according to my testing
|
|
||||||
* on netflix) completely transparent, this means we can determine whether
|
|
||||||
* the video is DRM-protected by looking at the alpha byte of the image.
|
|
||||||
*
|
|
||||||
* (Videos don't tend to have an alpha channel, so they're always
|
|
||||||
* completely opaque (i.e. have value of 255))
|
|
||||||
*/
|
|
||||||
hasDRM() {
|
|
||||||
// oh btw, there's one exception. There is this brief period between the point
|
|
||||||
// when metadata (video dimensions) have loaded and the moment the video starts
|
|
||||||
// playing where ctx.drawImage() will draw a transparent black square regardless
|
|
||||||
// of whether the video is actually DRM-protected or not.
|
|
||||||
if (! this.conf.hasVideoStartedPlaying()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return this.blackframeContext.getImageData(0,0,1,1).data[3] === 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
frameCheck(){
|
frameCheck(){
|
||||||
if(! this.video){
|
if(! this.video){
|
||||||
this.logger.log('error', 'debug', `%c[ArDetect::frameCheck] <@${this.arid}> Video went missing. Destroying current instance of videoData.`);
|
this.logger.log('error', 'debug', `%c[ArDetect::frameCheck] <@${this.arid}> Video went missing. Destroying current instance of videoData.`);
|
||||||
@ -547,7 +549,7 @@ class ArDetector {
|
|||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
var startTime = performance.now();
|
let startTime = performance.now();
|
||||||
let sampleCols = this.sampleCols.slice(0);
|
let sampleCols = this.sampleCols.slice(0);
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -557,10 +559,10 @@ class ArDetector {
|
|||||||
this.blackframeContext.drawImage(this.video, 0, 0, this.blackframeCanvas.width, this.blackframeCanvas.height);
|
this.blackframeContext.drawImage(this.video, 0, 0, this.blackframeCanvas.width, this.blackframeCanvas.height);
|
||||||
|
|
||||||
// special browsers require special tests
|
// special browsers require special tests
|
||||||
if (this.hasDRM()) {
|
// if (this.hasDRM()) {
|
||||||
this.fallbackMode = false;
|
// this.fallbackMode = false;
|
||||||
throw 'VIDEO_DRM_PROTECTED';
|
// throw 'VIDEO_DRM_PROTECTED';
|
||||||
}
|
// }
|
||||||
this.fallbackMode = false;
|
this.fallbackMode = false;
|
||||||
} catch (e) {
|
} 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);
|
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);
|
||||||
@ -569,17 +571,17 @@ class ArDetector {
|
|||||||
// ratio setting part of this function. For the time being, we're only stopping
|
// 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
|
// in case we encounter DRM error in Chrome. Firefox has fallback mode and generates
|
||||||
// different error, so that goes.
|
// different error, so that goes.
|
||||||
if (e === 'VIDEO_DRM_PROTECTED') {
|
// if (e === 'VIDEO_DRM_PROTECTED') {
|
||||||
// nothing to see here, really, if fallback mode isn't supported by browser
|
// // nothing to see here, really, if fallback mode isn't supported by browser
|
||||||
if (!this.drmNotificationShown) {
|
// if (!this.drmNotificationShown) {
|
||||||
this.drmNotificationShown = true;
|
// this.drmNotificationShown = true;
|
||||||
|
|
||||||
this.conf.player.showNotification('AARD_DRM');
|
// this.conf.player.showNotification('AARD_DRM');
|
||||||
this.conf.resizer.setAr({type: AspectRatio.Reset});
|
// this.conf.resizer.setAr({type: AspectRatio.Reset});
|
||||||
}
|
// }
|
||||||
|
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (! this.canvasReadyForDrawWindow()) {
|
if (! this.canvasReadyForDrawWindow()) {
|
||||||
// this means canvas needs to be resized, so we'll just re-run setup with all those new parameters
|
// this means canvas needs to be resized, so we'll just re-run setup with all those new parameters
|
||||||
@ -588,9 +590,9 @@ class ArDetector {
|
|||||||
let newCanvasWidth = window.innerHeight * (this.video.videoWidth / this.video.videoHeight);
|
let newCanvasWidth = window.innerHeight * (this.video.videoWidth / this.video.videoHeight);
|
||||||
let newCanvasHeight = window.innerHeight;
|
let newCanvasHeight = window.innerHeight;
|
||||||
|
|
||||||
if (this.conf.resizer.videoAlignment === VideoAlignment.Center) {
|
if (this.conf.resizer.videoAlignment === VideoAlignmentType.Center) {
|
||||||
this.canvasDrawWindowHOffset = Math.round((window.innerWidth - newCanvasWidth) * 0.5);
|
this.canvasDrawWindowHOffset = Math.round((window.innerWidth - newCanvasWidth) * 0.5);
|
||||||
} else if (this.conf.resizer.videoAlignment === VideoAlignment.Left) {
|
} else if (this.conf.resizer.videoAlignment === VideoAlignmentType.Left) {
|
||||||
this.canvasDrawWindowHOffset = 0;
|
this.canvasDrawWindowHOffset = 0;
|
||||||
} else {
|
} else {
|
||||||
this.canvasDrawWindowHOffset = window.innerWidth - newCanvasWidth;
|
this.canvasDrawWindowHOffset = window.innerWidth - newCanvasWidth;
|
||||||
@ -605,7 +607,7 @@ class ArDetector {
|
|||||||
this.fallbackMode = true;
|
this.fallbackMode = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.context.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, "rgba(0,0,128,1)");
|
||||||
} catch (e) {
|
} 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);
|
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
|
return; // it's prolly just a fluke, so we do nothing special here
|
||||||
@ -637,7 +639,7 @@ class ArDetector {
|
|||||||
// da je letterbox izginil.
|
// 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
|
// 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.
|
// have been corrected manually. It's also possible that letterbox (that was there before) disappeared.
|
||||||
this.conf.resizer.updateAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
|
this.conf.resizer.updateAr({type: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
|
||||||
this.guardLine.reset();
|
this.guardLine.reset();
|
||||||
this.noLetterboxCanvasReset = true;
|
this.noLetterboxCanvasReset = true;
|
||||||
|
|
||||||
@ -670,14 +672,16 @@ class ArDetector {
|
|||||||
// otherwise we continue. We add blackbar violations to the list of the cols
|
// otherwise we continue. We add blackbar violations to the list of the cols
|
||||||
// we'll sample and sort them
|
// we'll sample and sort them
|
||||||
if (guardLineOut.blackbarFail) {
|
if (guardLineOut.blackbarFail) {
|
||||||
sampleCols.concat(guardLineOut.offenders).sort((a, b) => a > b);
|
sampleCols.concat(guardLineOut.offenders).sort(
|
||||||
|
(a: number, b: number) => a - b
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're in fallback mode and blackbar test failed, we restore CSS and quit
|
// if we're in fallback mode and blackbar test failed, we restore CSS and quit
|
||||||
// (since the new letterbox edge isn't present in our sample due to technical
|
// (since the new letterbox edge isn't present in our sample due to technical
|
||||||
// limitations)
|
// limitations)
|
||||||
if (this.fallbackMode && guardLineOut.blackbarFail) {
|
if (this.fallbackMode && guardLineOut.blackbarFail) {
|
||||||
this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
|
this.conf.resizer.setAr({type: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
|
||||||
this.guardLine.reset();
|
this.guardLine.reset();
|
||||||
this.noLetterboxCanvasReset = true;
|
this.noLetterboxCanvasReset = true;
|
||||||
|
|
||||||
@ -697,17 +701,15 @@ class ArDetector {
|
|||||||
// that we will cut too much, we rather avoid doing anything at all. There's gonna be a next chance.
|
// that we will cut too much, we rather avoid doing anything at all. There's gonna be a next chance.
|
||||||
try{
|
try{
|
||||||
if(guardLineOut.blackbarFail || guardLineOut.imageFail){
|
if(guardLineOut.blackbarFail || guardLineOut.imageFail){
|
||||||
if(this.edgeDetector.findBars(imageData, null, EdgeDetectPrimaryDirection.HORIZONTAL).status === 'ar_known'){
|
if(this.edgeDetector.findBars(imageData, null, EdgeDetectPrimaryDirection.Horizontal).status === 'ar_known'){
|
||||||
|
|
||||||
|
|
||||||
if(guardLineOut.blackbarFail){
|
if(guardLineOut.blackbarFail){
|
||||||
this.logger.log('info', 'arDetect', `[ArDetect::frameCheck] Detected blackbar violation and pillarbox. Resetting to default aspect ratio.`);
|
this.logger.log('info', 'arDetect', `[ArDetect::frameCheck] Detected blackbar violation and pillarbox. Resetting to default aspect ratio.`);
|
||||||
this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
|
this.conf.resizer.setAr({type: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
|
||||||
this.guardLine.reset();
|
this.guardLine.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerTimeout = this.getTimeout(baseTimeout, startTime);
|
|
||||||
this.scheduleFrameCheck(triggerTimeout);
|
|
||||||
this.clearImageData(imageData);
|
this.clearImageData(imageData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -722,20 +724,20 @@ class ArDetector {
|
|||||||
|
|
||||||
// blackSamples -> {res_top, res_bottom}
|
// blackSamples -> {res_top, res_bottom}
|
||||||
|
|
||||||
var edgePost = this.edgeDetector.findBars(imageData, sampleCols, EdgeDetectPrimaryDirection.VERTICAL, EdgeDetectQuality.IMPROVED, guardLineOut, bfanalysis);
|
let edgePost = this.edgeDetector.findBars(imageData, sampleCols, EdgeDetectPrimaryDirection.Vertical, EdgeDetectQuality.Improved, guardLineOut, bfanalysis);
|
||||||
|
|
||||||
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] edgeDetector returned this\n`, "color: #aaf", edgePost);
|
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] edgeDetector returned this\n`, "color: #aaf", edgePost);
|
||||||
|
|
||||||
if (edgePost.status !== EdgeStatus.AR_KNOWN){
|
if (edgePost.status !== EdgeStatus.ARKnown){
|
||||||
// rob ni bil zaznan, zato ne naredimo ničesar.
|
// rob ni bil zaznan, zato ne naredimo ničesar.
|
||||||
// no edge was detected. Let's leave things as they were
|
// no edge was detected. Let's leave things as they were
|
||||||
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Edge wasn't detected with findBars`, "color: #fa3", edgePost, "EdgeStatus.AR_KNOWN:", EdgeStatus.AR_KNOWN);
|
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Edge wasn't detected with findBars`, "color: #fa3", edgePost, "EdgeStatus.AR_KNOWN:", EdgeStatus.ARKnown);
|
||||||
|
|
||||||
this.clearImageData(imageData);
|
this.clearImageData(imageData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newAr = this.calculateArFromEdges(edgePost);
|
let newAr = this.calculateArFromEdges(edgePost);
|
||||||
|
|
||||||
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Triggering aspect ration change! new ar: ${newAr}`, "color: #aaf");
|
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Triggering aspect ration change! new ar: ${newAr}`, "color: #aaf");
|
||||||
|
|
||||||
@ -767,14 +769,14 @@ class ArDetector {
|
|||||||
this.logger.log('error', 'arDetect', `%c[ArDetect::frameCheck] There was a problem setting blackbar. Doing nothing. Error:`, e);
|
this.logger.log('error', 'arDetect', `%c[ArDetect::frameCheck] There was a problem setting blackbar. Doing nothing. Error:`, e);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.guardline.reset();
|
this.guardLine.reset();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// no guardline, no bigge
|
// no guardline, no bigge
|
||||||
}
|
}
|
||||||
// WE DO NOT RESET ASPECT RATIO HERE IN CASE OF PROBLEMS, CAUSES UNWARRANTED RESETS:
|
// 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)
|
// (eg. here: https://www.youtube.com/watch?v=nw5Z93Yt-UQ&t=410)
|
||||||
//
|
//
|
||||||
// this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
|
// this.conf.resizer.setAr({type: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clearImageData(imageData);
|
this.clearImageData(imageData);
|
||||||
@ -806,7 +808,7 @@ class ArDetector {
|
|||||||
let cumulativeValue = 0;
|
let cumulativeValue = 0;
|
||||||
let blackPixelCount = 0;
|
let blackPixelCount = 0;
|
||||||
const bfImageData = this.blackframeContext.getImageData(0, 0, cols, rows).data;
|
const bfImageData = this.blackframeContext.getImageData(0, 0, cols, rows).data;
|
||||||
const blackTreshold = this.blackLevel + this.settings.active.arDetect.blackbar.frameTreshold;
|
const blackTreshold = this.blackLevel + this.settings.active.arDetect.blackbar.frameThreshold;
|
||||||
|
|
||||||
|
|
||||||
// we do some recon for letterbox and pillarbox. While this can't determine whether letterbox/pillarbox exists
|
// we do some recon for letterbox and pillarbox. While this can't determine whether letterbox/pillarbox exists
|
||||||
@ -814,7 +816,7 @@ class ArDetector {
|
|||||||
let rowMax = new Array(rows).fill(0);
|
let rowMax = new Array(rows).fill(0);
|
||||||
let colMax = new Array(cols).fill(0);
|
let colMax = new Array(cols).fill(0);
|
||||||
|
|
||||||
let r, c;
|
let r: number, c: number;
|
||||||
|
|
||||||
|
|
||||||
for (let i = 0; i < bfImageData.length; i+= 4) {
|
for (let i = 0; i < bfImageData.length; i+= 4) {
|
||||||
@ -928,7 +930,7 @@ class ArDetector {
|
|||||||
|
|
||||||
// detect black level. if currentMax comes above blackbar + blackbar threshold, we know we aren't letterboxed
|
// detect black level. if currentMax comes above blackbar + blackbar threshold, we know we aren't letterboxed
|
||||||
|
|
||||||
for (var i = 0; i < sampleCols.length; ++i){
|
for (let i = 0; i < sampleCols.length; ++i){
|
||||||
colOffset_r = sampleCols[i] << 2;
|
colOffset_r = sampleCols[i] << 2;
|
||||||
colOffset_g = colOffset_r + 1;
|
colOffset_g = colOffset_r + 1;
|
||||||
colOffset_b = colOffset_r + 2;
|
colOffset_b = colOffset_r + 2;
|
||||||
@ -964,8 +966,8 @@ class ArDetector {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ard_console_stop = "background: #000; color: #f41";
|
let _ard_console_stop = "background: #000; color: #f41";
|
||||||
var _ard_console_start = "background: #000; color: #00c399";
|
let _ard_console_start = "background: #000; color: #00c399";
|
||||||
var _ard_console_change = "background: #000; color: #ff8";
|
let _ard_console_change = "background: #000; color: #ff8";
|
||||||
|
|
||||||
export default ArDetector;
|
export default ArDetector;
|
12
src/ext/lib/ar-detect/DrmDetecor.ts
Normal file
12
src/ext/lib/ar-detect/DrmDetecor.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Checks whether video we're trying to play is protected by DRM.
|
||||||
|
* @param {*} video video we're trying to check
|
||||||
|
*/
|
||||||
|
export function hasDrm(video) {
|
||||||
|
// if video is not playing, we cannot know whether autodetection will work or not
|
||||||
|
if (!video || !(video.currentTime > 0 && !video.paused && !video.ended && video.readyState > 2)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return video.mediaKeys instanceof MediaKeys;
|
||||||
|
}
|
@ -1,13 +1,33 @@
|
|||||||
import Debug from '../../conf/Debug';
|
import Debug from '../../conf/Debug';
|
||||||
|
import Settings from '../Settings';
|
||||||
|
import ArDetector from './ArDetector';
|
||||||
|
|
||||||
|
export type GuardLineBar = {
|
||||||
|
top?: number;
|
||||||
|
bottom?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ImageCheckResult = {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
class GuardLine {
|
class GuardLine {
|
||||||
|
blackbar: GuardLineBar;
|
||||||
|
imageBar: GuardLineBar;
|
||||||
|
|
||||||
|
aard: ArDetector;
|
||||||
|
settings: Settings;
|
||||||
|
|
||||||
|
blackbarThreshold: number;
|
||||||
|
imageThreshold: number;
|
||||||
|
|
||||||
|
|
||||||
// ardConf — reference to ArDetector that has current GuardLine instance
|
// ardConf — reference to ArDetector that has current GuardLine instance
|
||||||
constructor(ardConf){
|
constructor (ardConf){
|
||||||
this.blackbar = {top: undefined, bottom: undefined};
|
this.blackbar = {top: undefined, bottom: undefined};
|
||||||
this.imageBar = {top: undefined, bottom: undefined};
|
this.imageBar = {top: undefined, bottom: undefined};
|
||||||
|
|
||||||
this.conf = ardConf;
|
this.aard = ardConf;
|
||||||
this.settings = ardConf.settings;
|
this.settings = ardConf.settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,12 +48,12 @@ class GuardLine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setBlackbar(bbconf){
|
setBlackbar(bbconf){
|
||||||
var bbTop = bbconf.top - this.settings.active.arDetect.guardLine.edgeTolerancePx;
|
let bbTop = bbconf.top - this.settings.active.arDetect.guardLine.edgeTolerancePx;
|
||||||
var bbBottom = bbconf.bottom + this.settings.active.arDetect.guardLine.edgeTolerancePx;
|
let bbBottom = bbconf.bottom + this.settings.active.arDetect.guardLine.edgeTolerancePx;
|
||||||
|
|
||||||
// to odstrani vse neveljavne nastavitve in vse možnosti, ki niso smiselne
|
// to odstrani vse neveljavne nastavitve in vse možnosti, ki niso smiselne
|
||||||
// this removes any configs with invalid values or values that dont make sense
|
// this removes any configs with invalid values or values that dont make sense
|
||||||
if (bbTop < 0 || bbBottom >= this.conf.canvas.height ){
|
if (bbTop < 0 || bbBottom >= this.aard.canvas.height ){
|
||||||
throw {error: "INVALID_SETTINGS_IN_GUARDLINE", bbTop, bbBottom}
|
throw {error: "INVALID_SETTINGS_IN_GUARDLINE", bbTop, bbBottom}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,12 +71,12 @@ class GuardLine {
|
|||||||
check(image, fallbackMode){
|
check(image, fallbackMode){
|
||||||
// izračunaj enkrat in shrani na objekt
|
// izračunaj enkrat in shrani na objekt
|
||||||
// calculate once and save object-instance-wide
|
// calculate once and save object-instance-wide
|
||||||
this.blackbarThreshold = this.conf.blackLevel + this.settings.active.arDetect.blackbar.threshold;
|
this.blackbarThreshold = this.aard.blackLevel + this.settings.active.arDetect.blackbar.threshold;
|
||||||
this.imageThreshold = this.blackbarThreshold + this.settings.active.arDetect.blackbar.imageThreshold;
|
this.imageThreshold = this.blackbarThreshold + this.settings.active.arDetect.blackbar.imageThreshold;
|
||||||
|
|
||||||
// dejansko testiranje
|
// dejansko testiranje
|
||||||
// actual checks
|
// actual checks
|
||||||
var guardLineResult = this.guardLineCheck(image, fallbackMode);
|
let guardLineResult = this.guardLineCheck(image, fallbackMode);
|
||||||
|
|
||||||
// Zaznali smo kršitev črnega dela, zato nam ni treba preveriti, ali je slika
|
// Zaznali smo kršitev črnega dela, zato nam ni treba preveriti, ali je slika
|
||||||
// prisotna. Vemo namreč, da se je razmerje stranic zmanjšalo.
|
// prisotna. Vemo namreč, da se je razmerje stranic zmanjšalo.
|
||||||
@ -71,7 +91,7 @@ class GuardLine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var imageCheckResult = this.imageCheck(image, fallbackMode);
|
let imageCheckResult = this.imageCheck(image, fallbackMode);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
blackbarFail: false,
|
blackbarFail: false,
|
||||||
@ -95,45 +115,46 @@ class GuardLine {
|
|||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
var offset = parseInt(this.conf.canvas.width * this.settings.active.arDetect.guardLine.ignoreEdgeMargin) << 2;
|
let offset = (this.aard.canvas.width * this.settings.active.arDetect.guardLine.ignoreEdgeMargin) << 2;
|
||||||
|
|
||||||
var offenders = [];
|
let offenders = [];
|
||||||
var offenderCount = -1; // doing it this way means first offender has offenderCount==0. Ez index.
|
let offenderCount = -1; // doing it this way means first offender has offenderCount==0. Ez index.
|
||||||
|
|
||||||
// TODO: implement logo check.
|
// TODO: implement logo check.
|
||||||
|
|
||||||
// preglejmo obe vrstici
|
// preglejmo obe vrstici
|
||||||
// check both rows
|
// check both rows
|
||||||
|
let edge_lower, edge_upper;
|
||||||
|
|
||||||
if(! fallbackMode){
|
if(! fallbackMode){
|
||||||
var edge_upper = this.blackbar.top;
|
edge_upper = this.blackbar.top;
|
||||||
var edge_lower = this.blackbar.bottom;
|
edge_lower = this.blackbar.bottom;
|
||||||
}
|
}
|
||||||
else{
|
else {
|
||||||
// fallback mode is a bit different
|
// fallback mode is a bit different
|
||||||
edge_upper = 0;
|
edge_upper = 0;
|
||||||
edge_lower = this.conf.canvas.height - 1;
|
edge_lower = this.aard.canvas.height - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var rowStart, rowEnd;
|
let rowStart, rowEnd;
|
||||||
|
|
||||||
// <<<=======| checking upper row |========>>>
|
// <<<=======| checking upper row |========>>>
|
||||||
|
|
||||||
rowStart = ((edge_upper * this.conf.canvas.width) << 2) + offset;
|
rowStart = ((edge_upper * this.aard.canvas.width) << 2) + offset;
|
||||||
rowEnd = rowStart + ( this.conf.canvas.width << 2 ) - (offset * 2);
|
rowEnd = rowStart + ( this.aard.canvas.width << 2 ) - (offset * 2);
|
||||||
|
|
||||||
if (Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine) {
|
if (Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine) {
|
||||||
offenderCount = this._gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount);
|
// offenderCount = this._gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount);
|
||||||
} else {
|
} else {
|
||||||
offenderCount = this._gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount);
|
offenderCount = this._gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount);
|
||||||
}
|
}
|
||||||
// <<<=======| checking lower row |========>>>
|
// <<<=======| checking lower row |========>>>
|
||||||
|
|
||||||
rowStart = ((edge_lower * this.conf.canvas.width) << 2) + offset;
|
rowStart = ((edge_lower * this.aard.canvas.width) << 2) + offset;
|
||||||
rowEnd = rowStart + ( this.conf.canvas.width << 2 ) - (offset * 2);
|
rowEnd = rowStart + ( this.aard.canvas.width << 2 ) - (offset * 2);
|
||||||
|
|
||||||
if (Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine) {
|
if (Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine) {
|
||||||
offenderCount = this._gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount);
|
// offenderCount = this._gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount);
|
||||||
} else {
|
} else {
|
||||||
offenderCount = this._gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount);
|
offenderCount = this._gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount);
|
||||||
}
|
}
|
||||||
@ -148,19 +169,19 @@ class GuardLine {
|
|||||||
return {success: true};
|
return {success: true};
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret = new Array(offenders.length);
|
let ret = new Array(offenders.length);
|
||||||
for(var o in offenders){
|
for(let o in offenders){
|
||||||
ret[o] = offenders[o].x + (offenders[o].width * 0.25);
|
ret[o] = offenders[o].x + (offenders[o].width * 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {success: false, offenders: ret};
|
return {success: false, offenders: ret};
|
||||||
}
|
}
|
||||||
|
|
||||||
imageCheck(image){
|
imageCheck(image, fallbackMode?: boolean): ImageCheckResult {
|
||||||
if(!this.imageBar.top || !this.imageBar.bottom)
|
if(!this.imageBar.top || !this.imageBar.bottom)
|
||||||
return { success: false };
|
return { success: false };
|
||||||
|
|
||||||
var offset = (this.conf.canvas.width * this.settings.active.arDetect.guardLine.ignoreEdgeMargin) << 2;
|
let offset = (this.aard.canvas.width * this.settings.active.arDetect.guardLine.ignoreEdgeMargin) << 2;
|
||||||
|
|
||||||
// TODO: implement logo check.
|
// TODO: implement logo check.
|
||||||
|
|
||||||
@ -169,47 +190,47 @@ class GuardLine {
|
|||||||
// check both rows - by the rules and definitions, we shouldn't go out of bounds here. no need to check, then
|
// check both rows - by the rules and definitions, we shouldn't go out of bounds here. no need to check, then
|
||||||
|
|
||||||
// if(fallbackMode){
|
// if(fallbackMode){
|
||||||
// var edge_upper = this.settings.active.arDetect.fallbackMode.noTriggerZonePx;
|
// let edge_upper = this.settings.active.arDetect.fallbackMode.noTriggerZonePx;
|
||||||
// var edge_lower = this.conf.canvas.height - this.settings.active.arDetect.fallbackMode.noTriggerZonePx - 1;
|
// let edge_lower = this.conf.canvas.height - this.settings.active.arDetect.fallbackMode.noTriggerZonePx - 1;
|
||||||
// }
|
// }
|
||||||
// else{
|
// else{
|
||||||
var edge_upper = this.imageBar.top;
|
let edge_upper = this.imageBar.top;
|
||||||
var edge_lower = this.imageBar.bottom;
|
let edge_lower = this.imageBar.bottom;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// koliko pikslov rabimo zaznati, da je ta funkcija uspe. Tu dovoljujemo tudi, da so vsi piksli na enem
|
// koliko pikslov rabimo zaznati, da je ta funkcija uspe. Tu dovoljujemo tudi, da so vsi piksli na enem
|
||||||
// robu (eden izmed robov je lahko v celoti črn)
|
// robu (eden izmed robov je lahko v celoti črn)
|
||||||
// how many non-black pixels we need to consider this check a success. We only need to detect enough pixels
|
// how many non-black pixels we need to consider this check a success. We only need to detect enough pixels
|
||||||
// on one edge (one of the edges can be black as long as both aren't)
|
// on one edge (one of the edges can be black as long as both aren't)
|
||||||
var successThreshold = (this.conf.canvas.width * this.settings.active.arDetect.guardLine.imageTestThreshold);
|
let successThreshold = (this.aard.canvas.width * this.settings.active.arDetect.guardLine.imageTestThreshold);
|
||||||
var rowStart, rowEnd;
|
let rowStart, rowEnd;
|
||||||
|
|
||||||
|
|
||||||
// <<<=======| checking upper row |========>>>
|
// <<<=======| checking upper row |========>>>
|
||||||
|
|
||||||
rowStart = ((edge_upper * this.conf.canvas.width) << 2) + offset;
|
rowStart = ((edge_upper * this.aard.canvas.width) << 2) + offset;
|
||||||
rowEnd = rowStart + ( this.conf.canvas.width << 2 ) - (offset * 2);
|
rowEnd = rowStart + ( this.aard.canvas.width << 2 ) - (offset * 2);
|
||||||
|
|
||||||
var res = false;
|
let res = false;
|
||||||
|
|
||||||
if(Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine){
|
if(Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine){
|
||||||
res = this._ti_debugCheckRow(image, rowStart, rowEnd, successThreshold);
|
// res = this._ti_debugCheckRow(image, rowStart, rowEnd, successThreshold);
|
||||||
} else {
|
} else {
|
||||||
res = this._ti_checkRow(image, rowStart, rowEnd,successThreshold);
|
res = this._ti_checkRow(image, rowStart, rowEnd,successThreshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(res)
|
if (res) {
|
||||||
return {success: true};
|
return {success: true};
|
||||||
|
}
|
||||||
|
|
||||||
// <<<=======| checking lower row |========>>>
|
// <<<=======| checking lower row |========>>>
|
||||||
|
|
||||||
rowStart = ((edge_lower * this.conf.canvas.width) << 2) + offset;
|
rowStart = ((edge_lower * this.aard.canvas.width) << 2) + offset;
|
||||||
// rowEnd = rowStart + ( this.conf.canvas.width << 2 ) - (offset * 2);
|
// rowEnd = rowStart + ( this.conf.canvas.width << 2 ) - (offset * 2);
|
||||||
|
|
||||||
|
|
||||||
if(Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine){
|
if(Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine){
|
||||||
res = this._ti_debugCheckRow(image, rowStart, rowEnd, successThreshold);
|
// res = this._ti_debugCheckRow(image, rowStart, rowEnd, successThreshold);
|
||||||
} else {
|
} else {
|
||||||
res = this._ti_checkRow(image, rowStart, rowEnd,successThreshold);
|
res = this._ti_checkRow(image, rowStart, rowEnd,successThreshold);
|
||||||
}
|
}
|
||||||
@ -222,8 +243,8 @@ class GuardLine {
|
|||||||
|
|
||||||
|
|
||||||
_gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount){
|
_gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount){
|
||||||
var firstOffender = -1;
|
let firstOffender = -1;
|
||||||
for(var i = rowStart; i < rowEnd; i+=4){
|
for(let i = rowStart; i < rowEnd; i+=4){
|
||||||
|
|
||||||
// we track sections that go over what's supposed to be a black line, so we can suggest more
|
// we track sections that go over what's supposed to be a black line, so we can suggest more
|
||||||
// columns to sample
|
// columns to sample
|
||||||
@ -246,36 +267,36 @@ class GuardLine {
|
|||||||
return offenderCount;
|
return offenderCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
_gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount){
|
// _gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount){
|
||||||
var firstOffender = -1;
|
// let firstOffender = -1;
|
||||||
for(var i = rowStart; i < rowEnd; i+=4){
|
// for(let i = rowStart; i < rowEnd; i+=4){
|
||||||
|
|
||||||
// we track sections that go over what's supposed to be a black line, so we can suggest more
|
// // we track sections that go over what's supposed to be a black line, so we can suggest more
|
||||||
// columns to sample
|
// // columns to sample
|
||||||
if(image[i] > this.blackbarThreshold || image[i+1] > this.blackbarThreshold || image[i+2] > this.blackbarThreshold){
|
// if(image[i] > this.blackbarThreshold || image[i+1] > this.blackbarThreshold || image[i+2] > this.blackbarThreshold){
|
||||||
this.conf.debugCanvas.trace(i, DebugCanvasClasses.VIOLATION);
|
// this.aard.debugCanvas.trace(i, DebugCanvasClasses.VIOLATION);
|
||||||
if(firstOffender < 0){
|
// if(firstOffender < 0){
|
||||||
firstOffender = (i - rowStart) >> 2;
|
// firstOffender = (i - rowStart) >> 2;
|
||||||
offenderCount++;
|
// offenderCount++;
|
||||||
offenders.push({x: firstOffender, width: 1});
|
// offenders.push({x: firstOffender, width: 1});
|
||||||
}
|
// }
|
||||||
else{
|
// else{
|
||||||
offenders[offenderCount].width++
|
// offenders[offenderCount].width++
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
else{
|
// else{
|
||||||
this.conf.debugCanvas.trace(i, DebugCanvasClasses.GUARDLINE_BLACKBAR);
|
// this.aard.debugCanvas.trace(i, DebugCanvasClasses.GUARDLINE_BLACKBAR);
|
||||||
// is that a black pixel again? Let's reset the 'first offender'
|
// // is that a black pixel again? Let's reset the 'first offender'
|
||||||
firstOffender = -1;
|
// firstOffender = -1;
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
// }
|
||||||
|
|
||||||
return offenderCount;
|
// return offenderCount;
|
||||||
}
|
// }
|
||||||
|
|
||||||
_ti_checkRow(image, rowStart, rowEnd, successThreshold) {
|
_ti_checkRow(image, rowStart, rowEnd, successThreshold): boolean {
|
||||||
for(var i = rowStart; i < rowEnd; i+=4){
|
for(let i = rowStart; i < rowEnd; i+=4){
|
||||||
if(image[i] > this.imageThreshold || image[i+1] > this.imageThreshold || image[i+2] > this.imageThreshold){
|
if(image[i] > this.imageThreshold || image[i+1] > this.imageThreshold || image[i+2] > this.imageThreshold){
|
||||||
if(successThreshold --<= 0){
|
if(successThreshold --<= 0){
|
||||||
return true;
|
return true;
|
||||||
@ -286,20 +307,20 @@ class GuardLine {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ti_debugCheckRow(image, rowStart, rowEnd, successThreshold) {
|
// _ti_debugCheckRow(image, rowStart, rowEnd, successThreshold) {
|
||||||
for(var i = rowStart; i < rowEnd; i+=4){
|
// for(let i = rowStart; i < rowEnd; i+=4){
|
||||||
if(image[i] > this.imageThreshold || image[i+1] > this.imageThreshold || image[i+2] > this.imageThreshold){
|
// if(image[i] > this.imageThreshold || image[i+1] > this.imageThreshold || image[i+2] > this.imageThreshold){
|
||||||
this.conf.debugCanvas.trace(i, DebugCanvasClasses.GUARDLINE_IMAGE);
|
// this.aard.debugCanvas.trace(i, DebugCanvasClasses.GUARDLINE_IMAGE);
|
||||||
if(successThreshold --<= 0){
|
// if(successThreshold --<= 0){
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
this.conf.debugCanvas.trace(i, DebugCanvasClasses.WARN);
|
// this.aard.debugCanvas.trace(i, DebugCanvasClasses.WARN);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GuardLine;
|
export default GuardLine;
|
@ -2,13 +2,28 @@ import Debug from '../../../conf/Debug';
|
|||||||
import EdgeStatus from './enums/EdgeStatusEnum';
|
import EdgeStatus from './enums/EdgeStatusEnum';
|
||||||
import EdgeDetectQuality from './enums/EdgeDetectQualityEnum';
|
import EdgeDetectQuality from './enums/EdgeDetectQualityEnum';
|
||||||
import EdgeDetectPrimaryDirection from './enums/EdgeDetectPrimaryDirectionEnum';
|
import EdgeDetectPrimaryDirection from './enums/EdgeDetectPrimaryDirectionEnum';
|
||||||
import AntiGradientMode from '../../../../common/enums/anti-gradient-mode.enum';
|
import AntiGradientMode from '../../../../common/enums/AntiGradientMode.enum';
|
||||||
|
import ArDetector from '../ArDetector';
|
||||||
|
import Logger from '../../Logger';
|
||||||
|
import Settings from '../../Settings';
|
||||||
|
|
||||||
if (Debug.debug) {
|
if (Debug.debug) {
|
||||||
console.log("Loading EdgeDetect.js");
|
console.log("Loading EdgeDetect.js");
|
||||||
}
|
}
|
||||||
|
|
||||||
class EdgeDetect{
|
class EdgeDetect{
|
||||||
|
conf: ArDetector;
|
||||||
|
logger: Logger;
|
||||||
|
settings: Settings;
|
||||||
|
|
||||||
|
|
||||||
|
// helper stuff
|
||||||
|
private sampleWidthBase: number;
|
||||||
|
private halfSample: number;
|
||||||
|
private detectionThreshold: number;
|
||||||
|
private colsThreshold: number;
|
||||||
|
private blackbarThreshold: number;
|
||||||
|
private imageThreshold: number;
|
||||||
|
|
||||||
constructor(ardConf){
|
constructor(ardConf){
|
||||||
this.conf = ardConf;
|
this.conf = ardConf;
|
||||||
@ -28,27 +43,27 @@ class EdgeDetect{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
findBars(image, sampleCols, direction = EdgeDetectPrimaryDirection.VERTICAL, quality = EdgeDetectQuality.IMPROVED, guardLineOut, blackFrameAnalysis){
|
findBars(image, sampleCols, direction = EdgeDetectPrimaryDirection.Vertical, quality = EdgeDetectQuality.Improved, guardLineOut?, blackFrameAnalysis?){
|
||||||
let fastCandidates, edgeCandidates, bars;
|
let fastCandidates, edgeCandidates, bars;
|
||||||
if (direction == EdgeDetectPrimaryDirection.VERTICAL) {
|
if (direction == EdgeDetectPrimaryDirection.Vertical) {
|
||||||
try {
|
try {
|
||||||
fastCandidates = this.findCandidates(image, sampleCols, guardLineOut);
|
fastCandidates = this.findCandidates(image, sampleCols, guardLineOut);
|
||||||
|
|
||||||
if (! this.isValidSample(fastCandidates)) {
|
if (! this.isValidSample(fastCandidates)) {
|
||||||
return {status: EdgeStatus.AR_UNKNOWN};
|
return {status: EdgeStatus.ARUnknown};
|
||||||
}
|
}
|
||||||
// if(quality == EdgeDetectQuality.FAST){
|
// if(quality == EdgeDetectQuality.FAST){
|
||||||
// edges = fastCandidates; // todo: processing
|
// edges = fastCandidates; // todo: processing
|
||||||
// } else {
|
// } else {
|
||||||
edgeCandidates = this.edgeDetect(image, fastCandidates);
|
edgeCandidates = this.edgeDetect(image, fastCandidates);
|
||||||
bars = this.edgePostprocess(edgeCandidates, this.conf.canvas.height);
|
bars = this.edgePostprocess(edgeCandidates);
|
||||||
// }
|
// }
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.log('error', 'arDetect', '%c[EdgeDetect::findBars] find bars failed.', 'background: #f00, color: #000', e);
|
this.logger.log('error', 'arDetect', '%c[EdgeDetect::findBars] find bars failed.', 'background: #f00, color: #000', e);
|
||||||
return {status: EdgeStatus.AR_UNKNOWN}
|
return {status: EdgeStatus.ARUnknown}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bars = this.pillarTest(image) ? {status: EdgeStatus.AR_KNOWN} : {status: EdgeStatus.AR_UNKNOWN};
|
bars = this.pillarTest(image) ? {status: EdgeStatus.ARKnown} : {status: EdgeStatus.ARUnknown};
|
||||||
}
|
}
|
||||||
|
|
||||||
return bars;
|
return bars;
|
||||||
@ -113,10 +128,10 @@ class EdgeDetect{
|
|||||||
|
|
||||||
this.logger.log('info', 'arDetect', '[EdgeDetect::findCandidates] searching for candidates on ranges', upper_top, '<->', upper_bottom, ';', lower_top, '<->', lower_bottom);
|
this.logger.log('info', 'arDetect', '[EdgeDetect::findCandidates] searching for candidates on ranges', upper_top, '<->', upper_bottom, ';', lower_top, '<->', lower_bottom);
|
||||||
|
|
||||||
var upper_top_corrected = upper_top * this.conf.canvasImageDataRowLength;
|
let upper_top_corrected = upper_top * this.conf.canvasImageDataRowLength;
|
||||||
var upper_bottom_corrected = upper_bottom * this.conf.canvasImageDataRowLength;
|
let upper_bottom_corrected = upper_bottom * this.conf.canvasImageDataRowLength;
|
||||||
var lower_top_corrected = lower_top * this.conf.canvasImageDataRowLength;
|
let lower_top_corrected = lower_top * this.conf.canvasImageDataRowLength;
|
||||||
var lower_bottom_corrected = lower_bottom * this.conf.canvasImageDataRowLength;
|
let lower_bottom_corrected = lower_bottom * this.conf.canvasImageDataRowLength;
|
||||||
|
|
||||||
// if(Debug.debugCanvas.enabled){
|
// if(Debug.debugCanvas.enabled){
|
||||||
// this._columnTest_dbgc(image, upper_top_corrected, upper_bottom_corrected, cols_a, res_top, false);
|
// this._columnTest_dbgc(image, upper_top_corrected, upper_bottom_corrected, cols_a, res_top, false);
|
||||||
@ -221,20 +236,20 @@ class EdgeDetect{
|
|||||||
}
|
}
|
||||||
|
|
||||||
edgeDetect(image, samples){
|
edgeDetect(image, samples){
|
||||||
var edgeCandidatesTop = {count: 0};
|
let edgeCandidatesTop = {count: 0};
|
||||||
var edgeCandidatesBottom = {count: 0};
|
let edgeCandidatesBottom = {count: 0};
|
||||||
|
|
||||||
var detections;
|
let detections;
|
||||||
var canvasWidth = this.conf.canvas.width;
|
let canvasWidth = this.conf.canvas.width;
|
||||||
var canvasHeight = this.conf.canvas.height;
|
let canvasHeight = this.conf.canvas.height;
|
||||||
|
|
||||||
var sampleStart, sampleEnd, loopEnd;
|
let sampleStart, sampleEnd, loopEnd;
|
||||||
var sampleRow_black, sampleRow_color;
|
let sampleRow_black, sampleRow_color;
|
||||||
|
|
||||||
var blackEdgeViolation = false;
|
let blackEdgeViolation = false;
|
||||||
|
|
||||||
var topEdgeCount = 0;
|
let topEdgeCount = 0;
|
||||||
var bottomEdgeCount = 0;
|
let bottomEdgeCount = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (const sample of samples.res_top){
|
for (const sample of samples.res_top){
|
||||||
@ -259,7 +274,7 @@ class EdgeDetect{
|
|||||||
loopEnd = sampleRow_black + sampleEnd;
|
loopEnd = sampleRow_black + sampleEnd;
|
||||||
|
|
||||||
if (Debug.debugCanvas.enabled){
|
if (Debug.debugCanvas.enabled){
|
||||||
blackEdgeViolation = this._blackbarTest_dbg(image, sampleRow_black + sampleStart, loopEnd);
|
// blackEdgeViolation = this._blackbarTest_dbg(image, sampleRow_black + sampleStart, loopEnd);
|
||||||
} else {
|
} else {
|
||||||
blackEdgeViolation = this._blackbarTest(image, sampleRow_black + sampleStart, loopEnd);
|
blackEdgeViolation = this._blackbarTest(image, sampleRow_black + sampleStart, loopEnd);
|
||||||
}
|
}
|
||||||
@ -274,7 +289,7 @@ class EdgeDetect{
|
|||||||
loopEnd = sampleRow_color + sampleEnd;
|
loopEnd = sampleRow_color + sampleEnd;
|
||||||
|
|
||||||
if (Debug.debugCanvas.enabled) {
|
if (Debug.debugCanvas.enabled) {
|
||||||
this._imageTest_dbg(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesTop)
|
// this._imageTest_dbg(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesTop)
|
||||||
} else {
|
} else {
|
||||||
this._imageTest(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesTop);
|
this._imageTest(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesTop);
|
||||||
}
|
}
|
||||||
@ -302,7 +317,7 @@ class EdgeDetect{
|
|||||||
loopEnd = sampleRow_black + sampleEnd;
|
loopEnd = sampleRow_black + sampleEnd;
|
||||||
|
|
||||||
if(Debug.debugCanvas.enabled){
|
if(Debug.debugCanvas.enabled){
|
||||||
blackEdgeViolation = this._blackbarTest_dbg(image, sampleRow_black + sampleStart, loopEnd);
|
// blackEdgeViolation = this._blackbarTest_dbg(image, sampleRow_black + sampleStart, loopEnd);
|
||||||
} else {
|
} else {
|
||||||
blackEdgeViolation = this._blackbarTest(image, sampleRow_black + sampleStart, loopEnd);
|
blackEdgeViolation = this._blackbarTest(image, sampleRow_black + sampleStart, loopEnd);
|
||||||
}
|
}
|
||||||
@ -317,7 +332,7 @@ class EdgeDetect{
|
|||||||
loopEnd = sampleRow_color + sampleEnd;
|
loopEnd = sampleRow_color + sampleEnd;
|
||||||
|
|
||||||
if(Debug.debugCanvas.enabled) {
|
if(Debug.debugCanvas.enabled) {
|
||||||
this._imageTest_dbg(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesBottom);
|
// this._imageTest_dbg(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesBottom);
|
||||||
} else {
|
} else {
|
||||||
this._imageTest(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesBottom);
|
this._imageTest(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesBottom);
|
||||||
}
|
}
|
||||||
@ -335,11 +350,11 @@ class EdgeDetect{
|
|||||||
}
|
}
|
||||||
|
|
||||||
edgePostprocess(edges){
|
edgePostprocess(edges){
|
||||||
var edgesTop = [];
|
let edgesTop = [];
|
||||||
var edgesBottom = [];
|
let edgesBottom = [];
|
||||||
var alignMargin = this.conf.canvas.height * this.settings.active.arDetect.allowedMisaligned;
|
let alignMargin = this.conf.canvas.height * this.settings.active.arDetect.allowedMisaligned;
|
||||||
|
|
||||||
var missingEdge = edges.edgeCandidatesTopCount == 0 || edges.edgeCandidatesBottomCount == 0;
|
let missingEdge = edges.edgeCandidatesTopCount == 0 || edges.edgeCandidatesBottomCount == 0;
|
||||||
|
|
||||||
// pretvorimo objekt v tabelo
|
// pretvorimo objekt v tabelo
|
||||||
// convert objects to array
|
// convert objects to array
|
||||||
@ -348,8 +363,8 @@ class EdgeDetect{
|
|||||||
delete(edges.edgeCandidatesBottom.count);
|
delete(edges.edgeCandidatesBottom.count);
|
||||||
|
|
||||||
if( edges.edgeCandidatesTopCount > 0){
|
if( edges.edgeCandidatesTopCount > 0){
|
||||||
for(var e in edges.edgeCandidatesTop){
|
for(let e in edges.edgeCandidatesTop){
|
||||||
var edge = edges.edgeCandidatesTop[e];
|
let edge = edges.edgeCandidatesTop[e];
|
||||||
edgesTop.push({
|
edgesTop.push({
|
||||||
distance: edge.offset,
|
distance: edge.offset,
|
||||||
absolute: edge.offset,
|
absolute: edge.offset,
|
||||||
@ -359,8 +374,8 @@ class EdgeDetect{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( edges.edgeCandidatesBottomCount > 0){
|
if( edges.edgeCandidatesBottomCount > 0){
|
||||||
for(var e in edges.edgeCandidatesBottom){
|
for(let e in edges.edgeCandidatesBottom){
|
||||||
var edge = edges.edgeCandidatesBottom[e];
|
let edge = edges.edgeCandidatesBottom[e];
|
||||||
edgesBottom.push({
|
edgesBottom.push({
|
||||||
distance: this.conf.canvas.height - edge.offset,
|
distance: this.conf.canvas.height - edge.offset,
|
||||||
absolute: edge.offset,
|
absolute: edge.offset,
|
||||||
@ -388,13 +403,13 @@ class EdgeDetect{
|
|||||||
if( edgesTop[0].distance >= edgesBottom[0].distance - alignMargin &&
|
if( edgesTop[0].distance >= edgesBottom[0].distance - alignMargin &&
|
||||||
edgesTop[0].distance <= edgesBottom[0].distance + alignMargin ){
|
edgesTop[0].distance <= edgesBottom[0].distance + alignMargin ){
|
||||||
|
|
||||||
var blackbarWidth = edgesTop[0].distance > edgesBottom[0].distance ?
|
let blackbarWidth = edgesTop[0].distance > edgesBottom[0].distance ?
|
||||||
edgesTop[0].distance : edgesBottom[0].distance;
|
edgesTop[0].distance : edgesBottom[0].distance;
|
||||||
|
|
||||||
if (edgesTop[0].count + edgesBottom[0].count > this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold
|
if (edgesTop[0].count + edgesBottom[0].count > this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold
|
||||||
|| ( edgesTop[0].count > this.settings.active.arDetect.edgeDetection.confirmationThreshold && edgesBottom[0].count > this.settings.active.arDetect.edgeDetection.confirmationThreshold) ){
|
|| ( edgesTop[0].count > this.settings.active.arDetect.edgeDetection.confirmationThreshold && edgesBottom[0].count > this.settings.active.arDetect.edgeDetection.confirmationThreshold) ){
|
||||||
return {
|
return {
|
||||||
status: EdgeStatus.AR_KNOWN,
|
status: EdgeStatus.ARKnown,
|
||||||
blackbarWidth: blackbarWidth,
|
blackbarWidth: blackbarWidth,
|
||||||
guardLineTop: edgesTop[0].distance,
|
guardLineTop: edgesTop[0].distance,
|
||||||
guardLineBottom: edgesBottom[0].absolute,
|
guardLineBottom: edgesBottom[0].absolute,
|
||||||
@ -415,20 +430,20 @@ class EdgeDetect{
|
|||||||
// manj vzorcev kot navaden rob.
|
// manj vzorcev kot navaden rob.
|
||||||
|
|
||||||
if (edgesTop[0].length > 1){
|
if (edgesTop[0].length > 1){
|
||||||
var lowMargin = edgesBottom[0].distance - alignMargin;
|
let lowMargin = edgesBottom[0].distance - alignMargin;
|
||||||
var highMargin = edgesBottom[0].distance + alignMargin;
|
let highMargin = edgesBottom[0].distance + alignMargin;
|
||||||
|
|
||||||
for (var i = 1; i < edgesTop.length; i++){
|
for (let i = 1; i < edgesTop.length; i++){
|
||||||
if(edgesTop[i].distance >= lowMargin && edgesTop[i].distance <= highMargin){
|
if(edgesTop[i].distance >= lowMargin && edgesTop[i].distance <= highMargin){
|
||||||
// dobili smo dejanski rob. vrnimo ga
|
// dobili smo dejanski rob. vrnimo ga
|
||||||
// we found the actual edge. let's return that.
|
// we found the actual edge. let's return that.
|
||||||
var blackbarWidth = edgesTop[i].distance > edgesBottom[0].distance ?
|
let blackbarWidth = edgesTop[i].distance > edgesBottom[0].distance ?
|
||||||
edgesTop[i].distance : edgesBottom[0].distance;
|
edgesTop[i].distance : edgesBottom[0].distance;
|
||||||
|
|
||||||
if (edgesTop[i].count + edgesBottom[0].count > this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold
|
if (edgesTop[i].count + edgesBottom[0].count > this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold
|
||||||
|| (edgesTop[i].count > this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold && edgesBottom[0].count > this.settings.active.arDetect.edgeDetection.confirmationThreshold) ) {
|
|| (edgesTop[i].count > this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold && edgesBottom[0].count > this.settings.active.arDetect.edgeDetection.confirmationThreshold) ) {
|
||||||
return {
|
return {
|
||||||
status: EdgeStatus.AR_KNOWN,
|
status: EdgeStatus.ARKnown,
|
||||||
blackbarWidth: blackbarWidth,
|
blackbarWidth: blackbarWidth,
|
||||||
guardLineTop: edgesTop[i].distance,
|
guardLineTop: edgesTop[i].distance,
|
||||||
guardLineBottom: edgesBottom[0].absolute,
|
guardLineBottom: edgesBottom[0].absolute,
|
||||||
@ -446,20 +461,20 @@ class EdgeDetect{
|
|||||||
edgesBottom[0].count < this.conf.sampleCols.length * this.settings.active.arDetect.edgeDetection.logoThreshold){
|
edgesBottom[0].count < this.conf.sampleCols.length * this.settings.active.arDetect.edgeDetection.logoThreshold){
|
||||||
|
|
||||||
if(edgesBottom[0].length > 1){
|
if(edgesBottom[0].length > 1){
|
||||||
var lowMargin = edgesTop[0].distance - alignMargin;
|
let lowMargin = edgesTop[0].distance - alignMargin;
|
||||||
var highMargin = edgesTop[0].distance + alignMargin;
|
let highMargin = edgesTop[0].distance + alignMargin;
|
||||||
|
|
||||||
for(var i = 1; i < edgesBottom.length; i++){
|
for(let i = 1; i < edgesBottom.length; i++){
|
||||||
if (edgesBottom[i].distance >= lowMargin && edgesTop[i].distance <= highMargin) {
|
if (edgesBottom[i].distance >= lowMargin && edgesTop[i].distance <= highMargin) {
|
||||||
// dobili smo dejanski rob. vrnimo ga
|
// dobili smo dejanski rob. vrnimo ga
|
||||||
// we found the actual edge. let's return that.
|
// we found the actual edge. let's return that.
|
||||||
var blackbarWidth = edgesBottom[i].distance > edgesTop[0].distance ?
|
let blackbarWidth = edgesBottom[i].distance > edgesTop[0].distance ?
|
||||||
edgesBottom[i].distance : edgesTop[0].distance;
|
edgesBottom[i].distance : edgesTop[0].distance;
|
||||||
|
|
||||||
if (edgesTop[0].count + edgesBottom[i].count > this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold
|
if (edgesTop[0].count + edgesBottom[i].count > this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold
|
||||||
|| (edgesTop[0].count > this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold && edgesBottom[i].count > this.settings.active.arDetect.edgeDetection.confirmationThreshold)) {
|
|| (edgesTop[0].count > this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold && edgesBottom[i].count > this.settings.active.arDetect.edgeDetection.confirmationThreshold)) {
|
||||||
return {
|
return {
|
||||||
status: EdgeStatus.AR_KNOWN,
|
status: EdgeStatus.ARKnown,
|
||||||
blackbarWidth: blackbarWidth,
|
blackbarWidth: blackbarWidth,
|
||||||
guardLineTop: edgesTop[0].distance,
|
guardLineTop: edgesTop[0].distance,
|
||||||
guardLineBottom: edgesBottom[i].absolute,
|
guardLineBottom: edgesBottom[i].absolute,
|
||||||
@ -482,10 +497,10 @@ class EdgeDetect{
|
|||||||
const edgeDetectionThreshold = this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold;
|
const edgeDetectionThreshold = this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold;
|
||||||
|
|
||||||
if (edges.edgeCandidatesTopCount == 0 && edges.edgeCandidatesBottomCount != 0){
|
if (edges.edgeCandidatesTopCount == 0 && edges.edgeCandidatesBottomCount != 0){
|
||||||
for(var edge of edgesBottom){
|
for(let edge of edgesBottom){
|
||||||
if(edge.count >= edgeDetectionThreshold)
|
if(edge.count >= edgeDetectionThreshold)
|
||||||
return {
|
return {
|
||||||
status: EdgeStatus.AR_KNOWN,
|
status: EdgeStatus.ARKnown,
|
||||||
blackbarWidth: edge.distance,
|
blackbarWidth: edge.distance,
|
||||||
guardLineTop: null,
|
guardLineTop: null,
|
||||||
guardLineBottom: edge.bottom,
|
guardLineBottom: edge.bottom,
|
||||||
@ -496,10 +511,10 @@ class EdgeDetect{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (edges.edgeCandidatesTopCount != 0 && edges.edgeCandidatesBottomCount == 0){
|
if (edges.edgeCandidatesTopCount != 0 && edges.edgeCandidatesBottomCount == 0){
|
||||||
for(var edge of edgesTop){
|
for(let edge of edgesTop){
|
||||||
if(edge.count >= edgeDetectionThreshold)
|
if(edge.count >= edgeDetectionThreshold)
|
||||||
return {
|
return {
|
||||||
status: EdgeStatus.AR_KNOWN,
|
status: EdgeStatus.ARKnown,
|
||||||
blackbarWidth: edge.distance,
|
blackbarWidth: edge.distance,
|
||||||
guardLineTop: edge.top,
|
guardLineTop: edge.top,
|
||||||
guardLineBottom: null,
|
guardLineBottom: null,
|
||||||
@ -512,7 +527,7 @@ class EdgeDetect{
|
|||||||
}
|
}
|
||||||
// če pridemo do sem, nam ni uspelo nič. Razmerje stranic ni znano
|
// če pridemo do sem, nam ni uspelo nič. Razmerje stranic ni znano
|
||||||
// if we reach this bit, we have failed in determining aspect ratio. It remains unknown.
|
// if we reach this bit, we have failed in determining aspect ratio. It remains unknown.
|
||||||
return {status: EdgeStatus.AR_UNKNOWN}
|
return {status: EdgeStatus.ARUnknown}
|
||||||
}
|
}
|
||||||
|
|
||||||
pillarTest(image){
|
pillarTest(image){
|
||||||
@ -523,22 +538,22 @@ class EdgeDetect{
|
|||||||
// roughly centered, we return true. Otherwise we return false.
|
// roughly centered, we return true. Otherwise we return false.
|
||||||
// we also return true if we detect too much black
|
// we also return true if we detect too much black
|
||||||
|
|
||||||
var blackbarThreshold, upper, lower;
|
let blackbarThreshold, upper, lower;
|
||||||
blackbarThreshold = this.conf.blackLevel + this.settings.active.arDetect.blackbar.threshold;
|
blackbarThreshold = this.conf.blackLevel + this.settings.active.arDetect.blackbar.threshold;
|
||||||
|
|
||||||
|
|
||||||
var middleRowStart = (this.conf.canvas.height >> 1) * this.conf.canvas.width;
|
let middleRowStart = (this.conf.canvas.height >> 1) * this.conf.canvas.width;
|
||||||
var middleRowEnd = middleRowStart + this.conf.canvas.width - 1;
|
let middleRowEnd = middleRowStart + this.conf.canvas.width - 1;
|
||||||
|
|
||||||
var rowStart = middleRowStart << 2;
|
let rowStart = middleRowStart << 2;
|
||||||
var midpoint = (middleRowStart + (this.conf.canvas.width >> 1)) << 2
|
let midpoint = (middleRowStart + (this.conf.canvas.width >> 1)) << 2
|
||||||
var rowEnd = middleRowEnd << 2;
|
let rowEnd = middleRowEnd << 2;
|
||||||
|
|
||||||
var edge_left = -1, edge_right = -1;
|
let edge_left = -1, edge_right = -1;
|
||||||
|
|
||||||
// preverimo na levi strani
|
// preverimo na levi strani
|
||||||
// let's check for edge on the left side
|
// let's check for edge on the left side
|
||||||
for(var i = rowStart; i < midpoint; i+=4){
|
for(let i = rowStart; i < midpoint; i+=4){
|
||||||
if(image[i] > blackbarThreshold || image[i+1] > blackbarThreshold || image[i+2] > blackbarThreshold){
|
if(image[i] > blackbarThreshold || image[i+1] > blackbarThreshold || image[i+2] > blackbarThreshold){
|
||||||
edge_left = (i - rowStart) >> 2;
|
edge_left = (i - rowStart) >> 2;
|
||||||
break;
|
break;
|
||||||
@ -547,7 +562,7 @@ class EdgeDetect{
|
|||||||
|
|
||||||
// preverimo na desni strani
|
// preverimo na desni strani
|
||||||
// check on the right
|
// check on the right
|
||||||
for(var i = rowEnd; i > midpoint; i-= 4){
|
for(let i = rowEnd; i > midpoint; i-= 4){
|
||||||
if(image[i] > blackbarThreshold || image[i+1] > blackbarThreshold || image[i+2] > blackbarThreshold){
|
if(image[i] > blackbarThreshold || image[i+1] > blackbarThreshold || image[i+2] > blackbarThreshold){
|
||||||
edge_right = this.conf.canvas.width - ((i - rowStart) >> 2);
|
edge_right = this.conf.canvas.width - ((i - rowStart) >> 2);
|
||||||
break;
|
break;
|
||||||
@ -566,9 +581,9 @@ class EdgeDetect{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var edgeError = this.settings.active.arDetect.pillarTest.allowMisaligned;
|
let edgeError = this.settings.active.arDetect.pillarTest.allowMisaligned;
|
||||||
var error_low = 1 - edgeError;
|
let error_low = 1 - edgeError;
|
||||||
var error_hi = 1 + edgeError;
|
let error_hi = 1 + edgeError;
|
||||||
|
|
||||||
// če sta 'edge_left' in 'edge_right' podobna/v mejah merske napake, potem vrnemo true — lahko da smo našli logo na sredini zaslona
|
// če sta 'edge_left' in 'edge_right' podobna/v mejah merske napake, potem vrnemo true — lahko da smo našli logo na sredini zaslona
|
||||||
// if 'edge_left' and 'edge_right' are similar enough to each other, we return true. If we found a logo in a black frame, we could
|
// if 'edge_left' and 'edge_right' are similar enough to each other, we return true. If we found a logo in a black frame, we could
|
||||||
@ -848,13 +863,13 @@ class EdgeDetect{
|
|||||||
let tmpI;
|
let tmpI;
|
||||||
let lastTmpI = 0;
|
let lastTmpI = 0;
|
||||||
let edgeDetectCount = 0;
|
let edgeDetectCount = 0;
|
||||||
for(const c in colsOut) {
|
for(const c of colsOut) {
|
||||||
c.diffs = [];
|
c.diffs = [];
|
||||||
}
|
}
|
||||||
if (reverseSearchDirection) {
|
if (reverseSearchDirection) {
|
||||||
if (this.settings.active.arDetect.blackbar.antiGradientMode === AntiGradientMode.Disabled) {
|
if (this.settings.active.arDetect.blackbar.antiGradientMode === AntiGradientMode.Disabled) {
|
||||||
// todo: remove gradient detection code from this branch
|
// todo: remove gradient detection code from this branch
|
||||||
for(var i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){
|
for(let i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){
|
||||||
for(let c = 0; c < colsIn.length; c++){
|
for(let c = 0; c < colsIn.length; c++){
|
||||||
if (colsIn[c].blackFound && colsIn[c].imageFound) {
|
if (colsIn[c].blackFound && colsIn[c].imageFound) {
|
||||||
// če smo našli obe točki, potem ne pregledujemo več.
|
// če smo našli obe točki, potem ne pregledujemo več.
|
||||||
@ -908,7 +923,7 @@ class EdgeDetect{
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// anti-gradient detection
|
// anti-gradient detection
|
||||||
for(var i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){
|
for(let i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){
|
||||||
for(let c = 0; c < colsIn.length; c++){
|
for(let c = 0; c < colsIn.length; c++){
|
||||||
if (colsIn[c].blackFound && colsIn[c].imageFound) {
|
if (colsIn[c].blackFound && colsIn[c].imageFound) {
|
||||||
// če smo našli obe točki, potem ne pregledujemo več.
|
// če smo našli obe točki, potem ne pregledujemo več.
|
||||||
@ -995,7 +1010,7 @@ class EdgeDetect{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for(var i = top; i < bottom; i+= this.conf.canvasImageDataRowLength){
|
for(let i = top; i < bottom; i+= this.conf.canvasImageDataRowLength){
|
||||||
for(let c = 0; c < colsIn.length; c++){
|
for(let c = 0; c < colsIn.length; c++){
|
||||||
if (colsIn[c].blackFound && colsIn[c].imageFound) {
|
if (colsIn[c].blackFound && colsIn[c].imageFound) {
|
||||||
// če smo našli obe točki, potem ne pregledujemo več.
|
// če smo našli obe točki, potem ne pregledujemo več.
|
||||||
@ -1051,18 +1066,18 @@ class EdgeDetect{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_columnTest(image, top, bottom, colsIn, colsOut, reverseSearchDirection){
|
_columnTest(image, top: number, bottom: number, colsIn, colsOut, reverseSearchDirection){
|
||||||
var tmpI;
|
let tmpI;
|
||||||
if(reverseSearchDirection){
|
if(reverseSearchDirection){
|
||||||
for(var i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){
|
for(let i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){
|
||||||
for(var col of colsIn){
|
for(const col of colsIn){
|
||||||
tmpI = i + (col << 2);
|
tmpI = i + (col << 2);
|
||||||
|
|
||||||
if( image[tmpI] > this.blackbarThreshold ||
|
if( image[tmpI] > this.blackbarThreshold ||
|
||||||
image[tmpI + 1] > this.blackbarThreshold ||
|
image[tmpI + 1] > this.blackbarThreshold ||
|
||||||
image[tmpI + 2] > this.blackbarThreshold ){
|
image[tmpI + 2] > this.blackbarThreshold ){
|
||||||
|
|
||||||
var bottom = (i / this.conf.canvasImageDataRowLength) + 1;
|
const bottom = (i / this.conf.canvasImageDataRowLength) + 1;
|
||||||
colsOut.push({
|
colsOut.push({
|
||||||
col: col,
|
col: col,
|
||||||
bottom: bottom
|
bottom: bottom
|
||||||
@ -1074,8 +1089,8 @@ class EdgeDetect{
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for(var i = top; i < bottom; i+= this.conf.canvasImageDataRowLength){
|
for(let i = top; i < bottom; i+= this.conf.canvasImageDataRowLength){
|
||||||
for(var col of colsIn){
|
for(const col of colsIn){
|
||||||
tmpI = i + (col << 2);
|
tmpI = i + (col << 2);
|
||||||
|
|
||||||
if( image[tmpI] > this.blackbarThreshold ||
|
if( image[tmpI] > this.blackbarThreshold ||
|
||||||
@ -1095,65 +1110,65 @@ class EdgeDetect{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_columnTest_dbgc(image, top, bottom, colsIn, colsOut, reverseSearchDirection){
|
// _columnTest_dbgc(image, top, bottom, colsIn, colsOut, reverseSearchDirection){
|
||||||
var tmpI;
|
// let tmpI;
|
||||||
if(reverseSearchDirection){
|
// if(reverseSearchDirection){
|
||||||
for(var i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){
|
// for(let i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){
|
||||||
for(var col of colsIn){
|
// for(let col of colsIn){
|
||||||
tmpI = i + (col << 2);
|
// tmpI = i + (col << 2);
|
||||||
|
|
||||||
if( image[tmpI] > this.blackbarThreshold ||
|
// if( image[tmpI] > this.blackbarThreshold ||
|
||||||
image[tmpI + 1] > this.blackbarThreshold ||
|
// image[tmpI + 1] > this.blackbarThreshold ||
|
||||||
image[tmpI + 2] > this.blackbarThreshold ){
|
// image[tmpI + 2] > this.blackbarThreshold ){
|
||||||
|
|
||||||
var bottom = (i / this.conf.canvasImageDataRowLength) + 1;
|
// let bottom = (i / this.conf.canvasImageDataRowLength) + 1;
|
||||||
colsOut.push({
|
// colsOut.push({
|
||||||
col: col,
|
// col: col,
|
||||||
bottom: bottom
|
// bottom: bottom
|
||||||
});
|
// });
|
||||||
colsIn.splice(colsIn.indexOf(col), 1);
|
// colsIn.splice(colsIn.indexOf(col), 1);
|
||||||
this.conf.debugCanvas.trace(tmpI,DebugCanvasClasses.EDGEDETECT_CANDIDATE);
|
// this.conf.debugCanvas.trace(tmpI,DebugCanvasClasses.EDGEDETECT_CANDIDATE);
|
||||||
}
|
// }
|
||||||
else{
|
// else{
|
||||||
this.conf.debugCanvas.trace(tmpI, DebugCanvasClasses.EDGEDETECT_ONBLACK);
|
// this.conf.debugCanvas.trace(tmpI, DebugCanvasClasses.EDGEDETECT_ONBLACK);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if(colsIn.length < this.colsThreshold)
|
// if(colsIn.length < this.colsThreshold)
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
for(var i = top; i < bottom; i+= this.conf.canvasImageDataRowLength){
|
// for(let i = top; i < bottom; i+= this.conf.canvasImageDataRowLength){
|
||||||
for(var col of colsIn){
|
// for(let col of colsIn){
|
||||||
tmpI = i + (col << 2);
|
// tmpI = i + (col << 2);
|
||||||
|
|
||||||
if( image[tmpI] > this.blackbarThreshold ||
|
// if( image[tmpI] > this.blackbarThreshold ||
|
||||||
image[tmpI + 1] > this.blackbarThreshold ||
|
// image[tmpI + 1] > this.blackbarThreshold ||
|
||||||
image[tmpI + 2] > this.blackbarThreshold ){
|
// image[tmpI + 2] > this.blackbarThreshold ){
|
||||||
|
|
||||||
colsOut.push({
|
// colsOut.push({
|
||||||
col: col,
|
// col: col,
|
||||||
top: (i / this.conf.canvasImageDataRowLength) - 1
|
// top: (i / this.conf.canvasImageDataRowLength) - 1
|
||||||
});
|
// });
|
||||||
colsIn.splice(colsIn.indexOf(col), 1);
|
// colsIn.splice(colsIn.indexOf(col), 1);
|
||||||
this.conf.debugCanvas.trace(tmpI, DebugCanvasClasses.EDGEDETECT_CANDIDATE);
|
// this.conf.debugCanvas.trace(tmpI, DebugCanvasClasses.EDGEDETECT_CANDIDATE);
|
||||||
if(tmpI-1 > 0){
|
// if(tmpI-1 > 0){
|
||||||
this.conf.debugCanvas.trace(tmpI - 1, DebugCanvasClasses.EDGEDETECT_CANDIDATE_SECONDARY);
|
// this.conf.debugCanvas.trace(tmpI - 1, DebugCanvasClasses.EDGEDETECT_CANDIDATE_SECONDARY);
|
||||||
}
|
// }
|
||||||
if(tmpI+1 < image.length){
|
// if(tmpI+1 < image.length){
|
||||||
this.conf.debugCanvas.trace(tmpI + 1, DebugCanvasClasses.EDGEDETECT_CANDIDATE_SECONDARY);
|
// this.conf.debugCanvas.trace(tmpI + 1, DebugCanvasClasses.EDGEDETECT_CANDIDATE_SECONDARY);
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
this.conf.debugCanvas.trace(tmpI, DebugCanvasClasses.EDGEDETECT_ONBLACK);
|
// this.conf.debugCanvas.trace(tmpI, DebugCanvasClasses.EDGEDETECT_ONBLACK);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if(colsIn.length < this.colsThreshold)
|
// if(colsIn.length < this.colsThreshold)
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
_blackbarTest(image, start, end){
|
_blackbarTest(image, start, end){
|
||||||
for(var i = start; i < end; i += 4){
|
for(let i = start; i < end; i += 4){
|
||||||
if( image[i ] > this.blackbarThreshold ||
|
if( image[i ] > this.blackbarThreshold ||
|
||||||
image[i+1] > this.blackbarThreshold ||
|
image[i+1] > this.blackbarThreshold ||
|
||||||
image[i+2] > this.blackbarThreshold ){
|
image[i+2] > this.blackbarThreshold ){
|
||||||
@ -1163,25 +1178,25 @@ class EdgeDetect{
|
|||||||
return false; // no violation
|
return false; // no violation
|
||||||
}
|
}
|
||||||
|
|
||||||
_blackbarTest_dbg(image, start, end){
|
// _blackbarTest_dbg(image, start, end){
|
||||||
for(var i = start; i < end; i += 4){
|
// for(let i = start; i < end; i += 4){
|
||||||
if( image[i ] > this.blackbarThreshold ||
|
// if( image[i ] > this.blackbarThreshold ||
|
||||||
image[i+1] > this.blackbarThreshold ||
|
// image[i+1] > this.blackbarThreshold ||
|
||||||
image[i+2] > this.blackbarThreshold ){
|
// image[i+2] > this.blackbarThreshold ){
|
||||||
|
|
||||||
this.conf.debugCanvas.trace(i, DebugCanvasClasses.VIOLATION)
|
// this.conf.debugCanvas.trace(i, DebugCanvasClasses.VIOLATION)
|
||||||
return true;
|
// return true;
|
||||||
} else {
|
// } else {
|
||||||
this.conf.debugCanvas.trace(i, DebugCanvasClasses.EDGEDETECT_BLACKBAR)
|
// this.conf.debugCanvas.trace(i, DebugCanvasClasses.EDGEDETECT_BLACKBAR)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return false; // no violation
|
// return false; // no violation
|
||||||
}
|
// }
|
||||||
|
|
||||||
_imageTest(image, start, end, sampleOffset, edgeCandidates){
|
_imageTest(image, start, end, sampleOffset, edgeCandidates){
|
||||||
var detections = 0;
|
let detections = 0;
|
||||||
|
|
||||||
for (var i = start; i < end; i += 4){
|
for (let i = start; i < end; i += 4){
|
||||||
if (image[i ] > this.blackbarThreshold ||
|
if (image[i ] > this.blackbarThreshold ||
|
||||||
image[i+1] > this.blackbarThreshold ||
|
image[i+1] > this.blackbarThreshold ||
|
||||||
image[i+2] > this.blackbarThreshold ){
|
image[i+2] > this.blackbarThreshold ){
|
||||||
@ -1198,28 +1213,28 @@ class EdgeDetect{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_imageTest_dbg(image, start, end, sampleOffset, edgeCandidates){
|
// _imageTest_dbg(image, start, end, sampleOffset, edgeCandidates){
|
||||||
var detections = 0;
|
// let detections = 0;
|
||||||
|
|
||||||
for(var i = start; i < end; i += 4){
|
// for(let i = start; i < end; i += 4){
|
||||||
if( image[i ] > this.blackbarThreshold ||
|
// if( image[i ] > this.blackbarThreshold ||
|
||||||
image[i+1] > this.blackbarThreshold ||
|
// image[i+1] > this.blackbarThreshold ||
|
||||||
image[i+2] > this.blackbarThreshold ){
|
// image[i+2] > this.blackbarThreshold ){
|
||||||
++detections;
|
// ++detections;
|
||||||
this.conf.debugCanvas.trace(i, DebugCanvasClasses.EDGEDETECT_IMAGE);
|
// this.conf.debugCanvas.trace(i, DebugCanvasClasses.EDGEDETECT_IMAGE);
|
||||||
} else {
|
// } else {
|
||||||
this.conf.debugCanvas.trace(i, DebugCanvasClasses.WARN);
|
// this.conf.debugCanvas.trace(i, DebugCanvasClasses.WARN);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if(detections >= this.detectionThreshold){
|
// if(detections >= this.detectionThreshold){
|
||||||
if(edgeCandidates[sampleOffset] != undefined)
|
// if(edgeCandidates[sampleOffset] != undefined)
|
||||||
edgeCandidates[sampleOffset].count++;
|
// edgeCandidates[sampleOffset].count++;
|
||||||
else{
|
// else{
|
||||||
edgeCandidates[sampleOffset] = {offset: sampleOffset, count: 1};
|
// edgeCandidates[sampleOffset] = {offset: sampleOffset, count: 1};
|
||||||
edgeCandidates.count++;
|
// edgeCandidates.count++;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +0,0 @@
|
|||||||
var EdgeDetectPrimaryDirection = Object.freeze({
|
|
||||||
VERTICAL: 0,
|
|
||||||
HORIZONTAL: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
export default EdgeDetectPrimaryDirection;
|
|
@ -0,0 +1,6 @@
|
|||||||
|
enum EdgeDetectPrimaryDirection {
|
||||||
|
Vertical = 0,
|
||||||
|
Horizontal = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EdgeDetectPrimaryDirection;
|
@ -1,6 +0,0 @@
|
|||||||
var EdgeDetectQuality = Object.freeze({
|
|
||||||
FAST: 0,
|
|
||||||
IMPROVED: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
export default EdgeDetectQuality;
|
|
@ -0,0 +1,6 @@
|
|||||||
|
enum EdgeDetectQuality {
|
||||||
|
Fast = 0,
|
||||||
|
Improved = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EdgeDetectQuality;
|
@ -1,6 +0,0 @@
|
|||||||
var EdgeStatus = Object.freeze({
|
|
||||||
AR_UNKNOWN: 0,
|
|
||||||
AR_KNOWN: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default EdgeStatus;
|
|
@ -0,0 +1,6 @@
|
|||||||
|
enum EdgeStatus {
|
||||||
|
ARUnknown = 0,
|
||||||
|
ARKnown = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EdgeStatus;
|
@ -1,43 +0,0 @@
|
|||||||
import Debug from '../../conf/Debug';
|
|
||||||
import BrowserDetect from '../../conf/BrowserDetect';
|
|
||||||
|
|
||||||
if (process.env.CHANNEL !== 'stable'){
|
|
||||||
console.info("Loading Comms");
|
|
||||||
}
|
|
||||||
|
|
||||||
class Comms {
|
|
||||||
static async sendMessage(message){
|
|
||||||
|
|
||||||
if(BrowserDetect.firefox){
|
|
||||||
return browser.runtime.sendMessage(message);
|
|
||||||
} else {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
try{
|
|
||||||
if(BrowserDetect.edge){
|
|
||||||
browser.runtime.sendMessage(message, function(response){
|
|
||||||
var r = response;
|
|
||||||
resolve(r);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
chrome.runtime.sendMessage(message, function(response){
|
|
||||||
// Chrome/js shittiness mitigation — remove this line and an empty array will be returned
|
|
||||||
var r = response;
|
|
||||||
resolve(r);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(e){
|
|
||||||
reject(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.CHANNEL !== 'stable'){
|
|
||||||
console.info("Comms loaded");
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Comms;
|
|
19
src/ext/lib/comms/Comms.ts
Normal file
19
src/ext/lib/comms/Comms.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import Debug from '../../conf/Debug';
|
||||||
|
import BrowserDetect from '../../conf/BrowserDetect';
|
||||||
|
import { browser } from 'webextension-polyfill-ts';
|
||||||
|
|
||||||
|
if (process.env.CHANNEL !== 'stable'){
|
||||||
|
console.info("Loading Comms");
|
||||||
|
}
|
||||||
|
|
||||||
|
class Comms {
|
||||||
|
static async sendMessage(message){
|
||||||
|
browser.runtime.sendMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.CHANNEL !== 'stable'){
|
||||||
|
console.info("Comms loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Comms;
|
@ -1,19 +1,33 @@
|
|||||||
import Debug from '../../conf/Debug';
|
import Debug from '../../conf/Debug';
|
||||||
import BrowserDetect from '../../conf/BrowserDetect';
|
import BrowserDetect from '../../conf/BrowserDetect';
|
||||||
|
import Logger from '../Logger';
|
||||||
|
import { browser } from 'webextension-polyfill-ts';
|
||||||
|
import Settings from '../Settings';
|
||||||
|
|
||||||
if (process.env.CHANNEL !== 'stable'){
|
if (process.env.CHANNEL !== 'stable'){
|
||||||
console.info("Loading CommsClient");
|
console.info("Loading CommsClient");
|
||||||
}
|
}
|
||||||
|
|
||||||
class CommsClient {
|
class CommsClient {
|
||||||
|
commsId: string;
|
||||||
|
|
||||||
|
logger: Logger;
|
||||||
|
settings: any; // sus?
|
||||||
|
|
||||||
|
|
||||||
|
commands: {[x: string]: any[]};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_listener: (m: any) => void;
|
||||||
|
port: any;
|
||||||
|
|
||||||
|
|
||||||
constructor(name, logger, commands) {
|
constructor(name, logger, commands) {
|
||||||
|
try {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
|
|
||||||
if (BrowserDetect.firefox) {
|
this.port = browser.runtime.connect(null, {name: name});
|
||||||
this.port = browser.runtime.connect({name: name});
|
|
||||||
} else if (BrowserDetect.anyChromium) {
|
|
||||||
this.port = chrome.runtime.connect({name: name});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.onLogEnd(
|
this.logger.onLogEnd(
|
||||||
(history) => {
|
(history) => {
|
||||||
@ -32,6 +46,9 @@ class CommsClient {
|
|||||||
this.commsId = (Math.random() * 20).toFixed(0);
|
this.commsId = (Math.random() * 20).toFixed(0);
|
||||||
|
|
||||||
this.commands = commands;
|
this.commands = commands;
|
||||||
|
} catch (e) {
|
||||||
|
console.error("CONSTRUCOTR FAILED:", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
@ -60,34 +77,11 @@ class CommsClient {
|
|||||||
|
|
||||||
async sendMessage_nonpersistent(message){
|
async sendMessage_nonpersistent(message){
|
||||||
message = JSON.parse(JSON.stringify(message)); // vue quirk. We should really use vue store instead
|
message = JSON.parse(JSON.stringify(message)); // vue quirk. We should really use vue store instead
|
||||||
|
return browser.runtime.sendMessage(null, message, null);
|
||||||
if(BrowserDetect.firefox){
|
|
||||||
return browser.runtime.sendMessage(message)
|
|
||||||
} else {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
try{
|
|
||||||
if(BrowserDetect.edge){
|
|
||||||
browser.runtime.sendMessage(message, function(response){
|
|
||||||
var r = response;
|
|
||||||
resolve(r);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
chrome.runtime.sendMessage(message, function(response){
|
|
||||||
// Chrome/js shittiness mitigation — remove this line and an empty array will be returned
|
|
||||||
var r = response;
|
|
||||||
resolve(r);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(e){
|
|
||||||
reject(e);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: sus function — does it get any use?
|
||||||
async requestSettings(){
|
async requestSettings(){
|
||||||
this.logger.log('info', 'comms', "%c[CommsClient::requestSettings] sending request for congif!", "background: #11D; color: #aad");
|
this.logger.log('info', 'comms', "%c[CommsClient::requestSettings] sending request for congif!", "background: #11D; color: #aad");
|
||||||
|
|
||||||
@ -99,12 +93,12 @@ class CommsClient {
|
|||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.settings.active = JSON.parse(response.extensionConf);
|
this.settings = {active: JSON.parse(response.extensionConf)};
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendMessage(message) {
|
async sendMessage(message) {
|
||||||
await this.sendMessage_nonpersistent(message);
|
return this.sendMessage_nonpersistent(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerVideo(){
|
registerVideo(){
|
@ -1,21 +1,34 @@
|
|||||||
import Debug from '../../conf/Debug';
|
import Debug from '../../conf/Debug';
|
||||||
import BrowserDetect from '../../conf/BrowserDetect';
|
import BrowserDetect from '../../conf/BrowserDetect';
|
||||||
|
import Logger from '../Logger';
|
||||||
|
import Settings from '../Settings';
|
||||||
|
import { browser } from 'webextension-polyfill-ts';
|
||||||
|
import ExtensionMode from '../../../common/enums/ExtensionMode.enum';
|
||||||
|
|
||||||
|
|
||||||
class CommsServer {
|
class CommsServer {
|
||||||
|
server: any;
|
||||||
|
logger: Logger;
|
||||||
|
settings: Settings;
|
||||||
|
|
||||||
|
|
||||||
|
ports: {
|
||||||
|
[frame: string] : {
|
||||||
|
[port: string]: any
|
||||||
|
}
|
||||||
|
}[] = [];
|
||||||
|
popupPort: any;
|
||||||
|
|
||||||
|
commands: {[x: string]: ((a: any, b: any) => void | Promise<void>)[]}
|
||||||
|
|
||||||
constructor(server) {
|
constructor(server) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.logger = server.logger;
|
this.logger = server.logger;
|
||||||
this.settings = server.settings;
|
this.settings = server.settings;
|
||||||
this.ports = [];
|
|
||||||
this.popupPort = null;
|
this.popupPort = null;
|
||||||
|
|
||||||
if (BrowserDetect.firefox) {
|
browser.runtime.onConnect.addListener(p => this.onConnect(p));
|
||||||
browser.runtime.onConnect.addListener(p => this.onConnect(p));
|
browser.runtime.onMessage.addListener((m, sender) => this.processReceivedMessage_nonpersistent(m, sender));
|
||||||
browser.runtime.onMessage.addListener((m, sender) => this.processReceivedMessage_nonpersistent(m, sender));
|
|
||||||
} else {
|
|
||||||
chrome.runtime.onConnect.addListener(p => this.onConnect(p));
|
|
||||||
chrome.runtime.onMessage.addListener((m, sender, callback) => this.processReceivedMessage_nonpersistent(m, sender, callback));
|
|
||||||
}
|
|
||||||
|
|
||||||
// commands — functions that handle incoming messages
|
// commands — functions that handle incoming messages
|
||||||
// functions can have the following arguments, which are,
|
// functions can have the following arguments, which are,
|
||||||
@ -51,14 +64,6 @@ class CommsServer {
|
|||||||
'popup-set-selected-tab': [
|
'popup-set-selected-tab': [
|
||||||
(message) => this.server.setSelectedTab(message.selectedMenu, message.selectedSubitem),
|
(message) => this.server.setSelectedTab(message.selectedMenu, message.selectedSubitem),
|
||||||
],
|
],
|
||||||
'get-config': [
|
|
||||||
(message, port) => {
|
|
||||||
this.logger.log('info', 'comms', "CommsServer: received get-config. Active settings?", this.settings.active, "\n(settings:", this.settings, ")");
|
|
||||||
port.postMessage(
|
|
||||||
{cmd: "set-config", conf: this.settings.active, site: this.server.currentSite}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'has-video': [
|
'has-video': [
|
||||||
(message, port) => this.server.registerVideo(port.sender),
|
(message, port) => this.server.registerVideo(port.sender),
|
||||||
],
|
],
|
||||||
@ -74,28 +79,31 @@ class CommsServer {
|
|||||||
'replace-css': [
|
'replace-css': [
|
||||||
(message, sender) => this.server.replaceCss(message.oldCssString, message.newCssString, sender),
|
(message, sender) => this.server.replaceCss(message.oldCssString, message.newCssString, sender),
|
||||||
],
|
],
|
||||||
|
// 'get-config': [
|
||||||
|
// (message, port) => {
|
||||||
|
// this.logger.log('info', 'comms', "CommsServer: received get-config. Active settings?", this.settings.active, "\n(settings:", this.settings, ")");
|
||||||
|
// port.postMessage(
|
||||||
|
// {cmd: "set-config", conf: this.settings.active, site: this.server.currentSite}
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
'get-config': [
|
'get-config': [
|
||||||
(message, sender, sendResponse) => {
|
(message, sender) => {
|
||||||
if (BrowserDetect.firefox) {
|
var ret = {extensionConf: JSON.stringify(this.settings.active)};
|
||||||
var ret = {extensionConf: JSON.stringify(this.settings.active)};
|
this.logger.log('info', 'comms', "%c[CommsServer.js::processMessage_nonpersistent] Returning this:", "background-color: #11D; color: #aad", ret);
|
||||||
this.logger.log('info', 'comms', "%c[CommsServer.js::processMessage_nonpersistent] Returning this:", "background-color: #11D; color: #aad", ret);
|
Promise.resolve(ret);
|
||||||
Promise.resolve(ret);
|
|
||||||
} else {
|
|
||||||
sendResponse({extensionConf: JSON.stringify(this.settings.active)});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
'autoar-enable': [
|
'autoar-enable': [
|
||||||
() => {
|
() => {
|
||||||
this.settings.active.sites['@global'].autoar = "blacklist";
|
this.settings.active.sites['@global'].autoar = ExtensionMode.Enabled;
|
||||||
this.settings.save();
|
this.settings.save();
|
||||||
this.logger.log('info', 'comms', "[uw-bg] autoar set to enabled (blacklist). evidenz:", this.settings.active);
|
this.logger.log('info', 'comms', "[uw-bg] autoar set to enabled (blacklist). evidenz:", this.settings.active);
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
'autoar-disable': [
|
'autoar-disable': [
|
||||||
(message) => {
|
(message) => {
|
||||||
this.settings.active.sites['@global'].autoar = "disabled";
|
this.settings.active.sites['@global'].autoar = ExtensionMode.Disabled;
|
||||||
if (message.reason){
|
if (message.reason){
|
||||||
this.settings.active.arDetect.disabledReason = message.reason;
|
this.settings.active.arDetect.disabledReason = message.reason;
|
||||||
} else {
|
} else {
|
||||||
@ -111,7 +119,7 @@ class CommsServer {
|
|||||||
|
|
||||||
// set fairly liberal limit
|
// set fairly liberal limit
|
||||||
var timeout = message.timeout < 4 ? 4 : message.timeout;
|
var timeout = message.timeout < 4 ? 4 : message.timeout;
|
||||||
this.settings.active.arDetect.timer_playing = timeout;
|
this.settings.active.arDetect.timers.playing = timeout;
|
||||||
this.settings.save();
|
this.settings.save();
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -140,7 +148,7 @@ class CommsServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getCurrentTabHostname() {
|
async getCurrentTabHostname() {
|
||||||
const activeTab = await this._getActiveTab();
|
const activeTab = await this.activeTab;
|
||||||
|
|
||||||
if (!activeTab || activeTab.length < 1) {
|
if (!activeTab || activeTab.length < 1) {
|
||||||
this.logger.log('warn', 'comms', 'There is no active tab for some reason. activeTab:', activeTab);
|
this.logger.log('warn', 'comms', 'There is no active tab for some reason. activeTab:', activeTab);
|
||||||
@ -173,34 +181,28 @@ class CommsServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getActiveTab() {
|
get activeTab() {
|
||||||
if (BrowserDetect.firefox) {
|
return browser.tabs.query({currentWindow: true, active: true});
|
||||||
return await browser.tabs.query({currentWindow: true, active: true});
|
|
||||||
} else {
|
|
||||||
return await new Promise( (resolve, reject) => {
|
|
||||||
chrome.tabs.query({lastFocusedWindow: true, active: true}, function (res) {
|
|
||||||
resolve(res);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if port is NOT defined, send to all content scripts of a given frame
|
/**
|
||||||
// if port is defined, send just to that particular script of a given frame
|
* Sends a message to addon content scripts.
|
||||||
async sendToFrameContentScripts(message, tab, frame, port) {
|
* @param message message
|
||||||
|
* @param tab the tab we want to send the message to
|
||||||
|
* @param frame the frame within that tab that we want to send the message to
|
||||||
|
* @param port if defined, message will only be sent to that specific script, otherwise it gets sent to all scripts of a given frame
|
||||||
|
*/
|
||||||
|
async sendToFrameContentScripts(message, tab, frame, port?) {
|
||||||
if (port !== undefined) {
|
if (port !== undefined) {
|
||||||
// note: 'port' is _not_ shadowed here.
|
|
||||||
this.ports[tab][frame][port].postMessage(message);
|
this.ports[tab][frame][port].postMessage(message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const port in this.ports[tab][frame]) {
|
for (const framePort in this.ports[tab][frame]) {
|
||||||
// note: 'port' is shadowed here!
|
this.ports[tab][frame][framePort].postMessage(message);
|
||||||
this.ports[tab][frame][port].postMessage(message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendToFrame(message, tab, frame, port) {
|
async sendToFrame(message, tab, frame, port?) {
|
||||||
this.logger.log('info', 'comms', `%c[CommsServer::sendToFrame] attempting to send message to tab ${tab}, frame ${frame}`, "background: #dda; color: #11D", message);
|
this.logger.log('info', 'comms', `%c[CommsServer::sendToFrame] attempting to send message to tab ${tab}, frame ${frame}`, "background: #dda; color: #11D", message);
|
||||||
|
|
||||||
if (isNaN(tab)) {
|
if (isNaN(tab)) {
|
||||||
@ -233,7 +235,7 @@ class CommsServer {
|
|||||||
async sendToActive(message) {
|
async sendToActive(message) {
|
||||||
this.logger.log('info', 'comms', "%c[CommsServer::sendToActive] trying to send a message to active tab. Message:", "background: #dda; color: #11D", message);
|
this.logger.log('info', 'comms', "%c[CommsServer::sendToActive] trying to send a message to active tab. Message:", "background: #dda; color: #11D", message);
|
||||||
|
|
||||||
const tabs = await this._getActiveTab();
|
const tabs = await this.activeTab;
|
||||||
|
|
||||||
this.logger.log('info', 'comms', "[CommsServer::_sendToActive] currently active tab(s)?", tabs);
|
this.logger.log('info', 'comms', "[CommsServer::_sendToActive] currently active tab(s)?", tabs);
|
||||||
for (const frame in this.ports[tabs[0].id]) {
|
for (const frame in this.ports[tabs[0].id]) {
|
||||||
@ -280,7 +282,10 @@ class CommsServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async execCmd(message, portOrSender, sendResponse) {
|
// TODO: sendResponse seems redundant — it used to be a callback for
|
||||||
|
// chrome-based browsers, but browser polyfill doesn't do callback. Just
|
||||||
|
// awaits.
|
||||||
|
async execCmd(message, portOrSender, sendResponse?) {
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
'info', 'comms', '[CommsServer.js::execCmd] Received message', message,
|
'info', 'comms', '[CommsServer.js::execCmd] Received message', message,
|
||||||
". Port/sender:", portOrSender, "sendResponse:", sendResponse, "\nThere is ", this.commands[message.cmd]?.length ?? 0,
|
". Port/sender:", portOrSender, "sendResponse:", sendResponse, "\nThere is ", this.commands[message.cmd]?.length ?? 0,
|
||||||
@ -289,7 +294,7 @@ class CommsServer {
|
|||||||
if (this.commands[message.cmd]) {
|
if (this.commands[message.cmd]) {
|
||||||
for (const c of this.commands[message.cmd]) {
|
for (const c of this.commands[message.cmd]) {
|
||||||
try {
|
try {
|
||||||
await c(message, portOrSender, sendResponse);
|
await c(message, portOrSender);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.log('error', 'debug', "[CommsServer.js::execCmd] failed to execute command.", e)
|
this.logger.log('error', 'debug', "[CommsServer.js::execCmd] failed to execute command.", e)
|
||||||
}
|
}
|
||||||
@ -297,8 +302,8 @@ class CommsServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleMessage(message, portOrSender, sendResponse) {
|
async handleMessage(message, portOrSender) {
|
||||||
await this.execCmd(message, portOrSender, sendResponse);
|
await this.execCmd(message, portOrSender);
|
||||||
|
|
||||||
if (message.forwardToSameFramePort) {
|
if (message.forwardToSameFramePort) {
|
||||||
this.sendToFrameContentScripts(message, portOrSender.tab.id, portOrSender.frameId, message.port)
|
this.sendToFrameContentScripts(message, portOrSender.tab.id, portOrSender.frameId, message.port)
|
||||||
@ -323,10 +328,10 @@ class CommsServer {
|
|||||||
this.handleMessage(message, port)
|
this.handleMessage(message, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
processReceivedMessage_nonpersistent(message, sender, sendResponse){
|
processReceivedMessage_nonpersistent(message, sender){
|
||||||
this.logger.log('info', 'comms', "%c[CommsServer.js::processMessage_nonpersistent] Received message from background script!", "background-color: #11D; color: #aad", message, sender);
|
this.logger.log('info', 'comms', "%c[CommsServer.js::processMessage_nonpersistent] Received message from background script!", "background-color: #11D; color: #aad", message, sender);
|
||||||
|
|
||||||
this.handleMessage(message, sender, sendResponse);
|
this.handleMessage(message, sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
// chrome shitiness mitigation
|
// chrome shitiness mitigation
|
@ -100,7 +100,11 @@ class PlayerUi extends UI {
|
|||||||
|
|
||||||
//#region lifecycle
|
//#region lifecycle
|
||||||
replace(playerElement) {
|
replace(playerElement) {
|
||||||
super.replace(this.getUiConfig(playerElement));
|
try {
|
||||||
|
super.replace(this.getUiConfig(playerElement));
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.log('error', 'Couldn\'t replace player element for ui. Error:', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ class UI {
|
|||||||
) {
|
) {
|
||||||
this.interfaceId = interfaceId;
|
this.interfaceId = interfaceId;
|
||||||
this.commsConfig = commsConfig;
|
this.commsConfig = commsConfig;
|
||||||
this.storeConfig = storeConfig,
|
this.storeConfig = storeConfig;
|
||||||
this.uiConfig = uiConfig;
|
this.uiConfig = uiConfig;
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
|
@ -1,27 +1,55 @@
|
|||||||
import Debug from '../../conf/Debug';
|
import Debug from '../../conf/Debug';
|
||||||
import VideoData from './VideoData';
|
import VideoData from './VideoData';
|
||||||
import RescanReason from './enums/RescanReason';
|
import RescanReason from './enums/RescanReason.enum';
|
||||||
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
|
import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
|
||||||
import CropModePersistence from '../../../common/enums/crop-mode-persistence.enum';
|
import CropModePersistence from '../../../common/enums/CropModePersistence.enum';
|
||||||
|
import Logger from '../Logger';
|
||||||
|
import Settings from '../Settings';
|
||||||
|
import ExtensionMode from '../../../common/enums/ExtensionMode.enum';
|
||||||
|
import CommsClient from '../comms/CommsClient';
|
||||||
|
|
||||||
if (process.env.CHANNEL !== 'stable'){
|
if (process.env.CHANNEL !== 'stable'){
|
||||||
console.info("Loading PageInfo");
|
console.info("Loading PageInfo");
|
||||||
}
|
}
|
||||||
|
|
||||||
class PageInfo {
|
class PageInfo {
|
||||||
|
//#region flags
|
||||||
|
readOnly: boolean = false;
|
||||||
|
hasVideos: boolean = false;
|
||||||
|
siteDisabled: boolean = false;
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region timers and timeouts
|
||||||
|
rescanTimer: any;
|
||||||
|
urlCheckTimer: any;
|
||||||
|
announceZoomTimeout: any;
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region helper objects
|
||||||
|
logger: Logger;
|
||||||
|
settings: Settings;
|
||||||
|
comms: CommsClient;
|
||||||
|
videos: VideoData[] = [];
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region misc stuff
|
||||||
|
lastUrl: string;
|
||||||
|
extensionMode: ExtensionMode;
|
||||||
|
defaultCrop: any;
|
||||||
|
currentCrop: any;
|
||||||
|
actionHandlerInitQueue: any[] = [];
|
||||||
|
currentZoomScale: number = 1;
|
||||||
|
|
||||||
|
actionHandler: any;
|
||||||
|
//#endregion
|
||||||
|
|
||||||
constructor(comms, settings, logger, extensionMode, readOnly = false){
|
constructor(comms, settings, logger, extensionMode, readOnly = false){
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.hasVideos = false;
|
|
||||||
this.siteDisabled = false;
|
|
||||||
this.videos = [];
|
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.actionHandlerInitQueue = [];
|
|
||||||
|
|
||||||
this.lastUrl = window.location.href;
|
this.lastUrl = window.location.href;
|
||||||
this.extensionMode = extensionMode;
|
this.extensionMode = extensionMode;
|
||||||
this.readOnly = readOnly;
|
this.readOnly = readOnly;
|
||||||
this.defaultCrop = undefined;
|
|
||||||
this.currentCrop = undefined;
|
|
||||||
|
|
||||||
if (comms){
|
if (comms){
|
||||||
this.comms = comms;
|
this.comms = comms;
|
||||||
@ -54,8 +82,6 @@ class PageInfo {
|
|||||||
|
|
||||||
this.rescan(RescanReason.PERIODIC);
|
this.rescan(RescanReason.PERIODIC);
|
||||||
this.scheduleUrlCheck();
|
this.scheduleUrlCheck();
|
||||||
|
|
||||||
this.currentZoomScale = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async injectCss(cssString) {
|
async injectCss(cssString) {
|
||||||
@ -85,9 +111,9 @@ class PageInfo {
|
|||||||
if(this.rescanTimer){
|
if(this.rescanTimer){
|
||||||
clearTimeout(this.rescanTimer);
|
clearTimeout(this.rescanTimer);
|
||||||
}
|
}
|
||||||
for (var video of this.videos) {
|
for (let video of this.videos) {
|
||||||
try {
|
try {
|
||||||
this.comms.unregisterVideo(video.id)
|
(this.comms.unregisterVideo as any)(video.vdid)
|
||||||
video.destroy();
|
video.destroy();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.log('error', ['debug', 'init'], '[PageInfo::destroy] unable to destroy video! Error:', e);
|
this.logger.log('error', ['debug', 'init'], '[PageInfo::destroy] unable to destroy video! Error:', e);
|
||||||
@ -95,7 +121,7 @@ class PageInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
playerStyleString = this.settings.active.sites[window.location.hostname].css;
|
const playerStyleString = this.settings.active.sites[window.location.hostname].css;
|
||||||
if (playerStyleString) {
|
if (playerStyleString) {
|
||||||
this.comms.sendMessage({
|
this.comms.sendMessage({
|
||||||
cmd: 'eject-css',
|
cmd: 'eject-css',
|
||||||
@ -108,7 +134,7 @@ class PageInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
for(var video of this.videos) {
|
for(let video of this.videos) {
|
||||||
video.destroy();
|
video.destroy();
|
||||||
}
|
}
|
||||||
this.rescan(RescanReason.MANUAL);
|
this.rescan(RescanReason.MANUAL);
|
||||||
@ -124,7 +150,7 @@ class PageInfo {
|
|||||||
|
|
||||||
setActionHandler(actionHandler) {
|
setActionHandler(actionHandler) {
|
||||||
this.actionHandler = actionHandler;
|
this.actionHandler = actionHandler;
|
||||||
for (var item of this.actionHandlerInitQueue) {
|
for (let item of this.actionHandlerInitQueue) {
|
||||||
this.actionHandler.registerHandleMouse(item);
|
this.actionHandler.registerHandleMouse(item);
|
||||||
}
|
}
|
||||||
this.actionHandlerInitQueue = [];
|
this.actionHandlerInitQueue = [];
|
||||||
@ -132,8 +158,8 @@ class PageInfo {
|
|||||||
|
|
||||||
getVideos(host) {
|
getVideos(host) {
|
||||||
if (this.settings.active.sites[host]?.DOM?.video?.manual
|
if (this.settings.active.sites[host]?.DOM?.video?.manual
|
||||||
&& this.settings.active.sites[host]?.DOM?.video?.querySelector){
|
&& this.settings.active.sites[host]?.DOM?.video?.querySelectors){
|
||||||
const videos = document.querySelectorAll(this.settings.active.sites[host].DOM.video.querySelector);
|
const videos = document.querySelectorAll(this.settings.active.sites[host].DOM.video.querySelectors);
|
||||||
|
|
||||||
if (videos.length) {
|
if (videos.length) {
|
||||||
return videos;
|
return videos;
|
||||||
@ -150,7 +176,7 @@ class PageInfo {
|
|||||||
const oldVideoCount = this.videos.length;
|
const oldVideoCount = this.videos.length;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
var vids = this.getVideos(window.location.hostname);
|
let vids = this.getVideos(window.location.hostname);
|
||||||
|
|
||||||
if(!vids || vids.length == 0){
|
if(!vids || vids.length == 0){
|
||||||
this.hasVideos = false;
|
this.hasVideos = false;
|
||||||
@ -164,8 +190,8 @@ class PageInfo {
|
|||||||
|
|
||||||
// add new videos
|
// add new videos
|
||||||
this.hasVideos = false;
|
this.hasVideos = false;
|
||||||
var videoExists = false;
|
let videoExists = false;
|
||||||
var video, v;
|
let video, v;
|
||||||
|
|
||||||
for (video of vids) {
|
for (video of vids) {
|
||||||
// če najdemo samo en video z višino in širino, to pomeni, da imamo na strani veljavne videe
|
// če najdemo samo en video z višino in širino, to pomeni, da imamo na strani veljavne videe
|
||||||
@ -245,9 +271,11 @@ class PageInfo {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
if (this.videos.length > 0) {
|
if (this.videos.length > 0) {
|
||||||
this.comms.registerVideo({host: window.location.hostname, location: window.location});
|
// this.comms.registerVideo({host: window.location.hostname, location: window.location});
|
||||||
|
this.comms.registerVideo();
|
||||||
} else {
|
} else {
|
||||||
this.comms.unregisterVideo({host: window.location.hostname, location: window.location});
|
// this.comms.unregisterVideo({host: window.location.hostname, location: window.location});
|
||||||
|
this.comms.unregisterVideo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,7 +314,7 @@ class PageInfo {
|
|||||||
clearTimeout(this.rescanTimer);
|
clearTimeout(this.rescanTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
var ths = this;
|
let ths = this;
|
||||||
|
|
||||||
this.rescanTimer = setTimeout(function(rescanReason){
|
this.rescanTimer = setTimeout(function(rescanReason){
|
||||||
ths.rescanTimer = null;
|
ths.rescanTimer = null;
|
||||||
@ -304,7 +332,7 @@ class PageInfo {
|
|||||||
clearTimeout(this.urlCheckTimer);
|
clearTimeout(this.urlCheckTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
var ths = this;
|
let ths = this;
|
||||||
|
|
||||||
this.urlCheckTimer = setTimeout(function(){
|
this.urlCheckTimer = setTimeout(function(){
|
||||||
ths.urlCheckTimer = null;
|
ths.urlCheckTimer = null;
|
||||||
@ -329,14 +357,14 @@ class PageInfo {
|
|||||||
|
|
||||||
initArDetection(playingOnly){
|
initArDetection(playingOnly){
|
||||||
if (playingOnly) {
|
if (playingOnly) {
|
||||||
for(var vd of this.videos){
|
for(let vd of this.videos){
|
||||||
if(vd.isPlaying()) {
|
if(vd.isPlaying()) {
|
||||||
vd.initArDetection();
|
vd.initArDetection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
for(var vd of this.videos){
|
for(let vd of this.videos){
|
||||||
vd.initArDetection();
|
vd.initArDetection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -347,13 +375,13 @@ class PageInfo {
|
|||||||
// these need to be called on tab switch
|
// these need to be called on tab switch
|
||||||
pauseProcessing(playingOnly){
|
pauseProcessing(playingOnly){
|
||||||
if (playingOnly) {
|
if (playingOnly) {
|
||||||
for(var vd of this.videos){
|
for(let vd of this.videos){
|
||||||
if (vd.isPlaying()) {
|
if (vd.isPlaying()) {
|
||||||
vd.pause();
|
vd.pause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for(var vd of this.videos){
|
for(let vd of this.videos){
|
||||||
vd.pause();
|
vd.pause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -361,7 +389,7 @@ class PageInfo {
|
|||||||
|
|
||||||
resumeProcessing(resumeAutoar = false, playingOnly = false){
|
resumeProcessing(resumeAutoar = false, playingOnly = false){
|
||||||
if (playingOnly) {
|
if (playingOnly) {
|
||||||
for(var vd of this.videos){
|
for(let vd of this.videos){
|
||||||
if (vd.isPlaying()) {
|
if (vd.isPlaying()) {
|
||||||
vd.resume();
|
vd.resume();
|
||||||
if(resumeAutoar){
|
if(resumeAutoar){
|
||||||
@ -370,7 +398,7 @@ class PageInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for(var vd of this.videos){
|
for(let vd of this.videos){
|
||||||
vd.resume();
|
vd.resume();
|
||||||
if(resumeAutoar){
|
if(resumeAutoar){
|
||||||
vd.resumeAutoAr();
|
vd.resumeAutoAr();
|
||||||
@ -385,13 +413,13 @@ class PageInfo {
|
|||||||
this.logger.log('info', 'debug', '[PageInfo::startArDetection()] starting automatic ar detection!')
|
this.logger.log('info', 'debug', '[PageInfo::startArDetection()] starting automatic ar detection!')
|
||||||
}
|
}
|
||||||
if (playingOnly) {
|
if (playingOnly) {
|
||||||
for(var vd of this.videos){
|
for(let vd of this.videos){
|
||||||
if (video.isPlaying()) {
|
if (vd.isPlaying()) {
|
||||||
vd.startArDetection();
|
vd.startArDetection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for(var vd of this.videos){
|
for(let vd of this.videos){
|
||||||
vd.startArDetection();
|
vd.startArDetection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -399,28 +427,28 @@ class PageInfo {
|
|||||||
|
|
||||||
stopArDetection(playingOnly){
|
stopArDetection(playingOnly){
|
||||||
if (playingOnly) {
|
if (playingOnly) {
|
||||||
for(var vd of this.videos){
|
for(let vd of this.videos){
|
||||||
if (vd.isPlaying()) {
|
if (vd.isPlaying()) {
|
||||||
vd.stopArDetection();
|
vd.stopArDetection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for(var vd of this.videos){
|
for(let vd of this.videos){
|
||||||
vd.stopArDetection();
|
vd.stopArDetection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setAr(ar, playingOnly){
|
setAr(ar, playingOnly?: boolean){
|
||||||
this.logger.log('info', 'debug', '[PageInfo::setAr] aspect ratio:', ar, "playing only?", playingOnly)
|
this.logger.log('info', 'debug', '[PageInfo::setAr] aspect ratio:', ar, "playing only?", playingOnly)
|
||||||
|
|
||||||
if (ar.type !== AspectRatio.Automatic) {
|
if (ar.type !== AspectRatioType.Automatic) {
|
||||||
this.stopArDetection(playingOnly);
|
this.stopArDetection(playingOnly);
|
||||||
} else {
|
} else {
|
||||||
this.logger.log('info', 'debug', '[PageInfo::setAr] aspect ratio is auto');
|
this.logger.log('info', 'debug', '[PageInfo::setAr] aspect ratio is auto');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (var vd of this.videos) {
|
for (let vd of this.videos) {
|
||||||
if (!playingOnly || vd.isPlaying()) {
|
if (!playingOnly || vd.isPlaying()) {
|
||||||
vd.resetLastAr();
|
vd.resetLastAr();
|
||||||
}
|
}
|
||||||
@ -434,14 +462,14 @@ class PageInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: find a way to only change aspect ratio for one video
|
// TODO: find a way to only change aspect ratio for one video
|
||||||
if (ar === AspectRatio.Reset) {
|
if (ar === AspectRatioType.Reset) {
|
||||||
for (var vd of this.videos) {
|
for (let vd of this.videos) {
|
||||||
if (!playingOnly || vd.isPlaying()) {
|
if (!playingOnly || vd.isPlaying()) {
|
||||||
vd.resetAr();
|
vd.resetAr();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (var vd of this.videos) {
|
for (let vd of this.videos) {
|
||||||
if (!playingOnly || vd.isPlaying()) {
|
if (!playingOnly || vd.isPlaying()) {
|
||||||
vd.setAr(ar)
|
vd.setAr(ar)
|
||||||
}
|
}
|
||||||
@ -451,78 +479,78 @@ class PageInfo {
|
|||||||
|
|
||||||
setVideoAlignment(videoAlignment, playingOnly) {
|
setVideoAlignment(videoAlignment, playingOnly) {
|
||||||
if (playingOnly) {
|
if (playingOnly) {
|
||||||
for(var vd of this.videos) {
|
for(let vd of this.videos) {
|
||||||
if (vd.isPlaying()) {
|
if (vd.isPlaying()) {
|
||||||
vd.setVideoAlignment(videoAlignment)
|
vd.setVideoAlignment(videoAlignment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for(var vd of this.videos) {
|
for(let vd of this.videos) {
|
||||||
vd.setVideoAlignment(videoAlignment)
|
vd.setVideoAlignment(videoAlignment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setPanMode(mode, playingOnly) {
|
setPanMode(mode, playingOnly?: boolean) {
|
||||||
if (playingOnly) {
|
if (playingOnly) {
|
||||||
for(var vd of this.videos) {
|
for(let vd of this.videos) {
|
||||||
if (vd.isPlaying()) {
|
if (vd.isPlaying()) {
|
||||||
vd.setPanMode(mode);
|
vd.setPanMode(mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for(var vd of this.videos) {
|
for(let vd of this.videos) {
|
||||||
vd.setPanMode(mode);
|
vd.setPanMode(mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreAr(playingOnly) {
|
restoreAr(playingOnly?: boolean) {
|
||||||
if (playingOnly) {
|
if (playingOnly) {
|
||||||
for(var vd of this.videos){
|
for(let vd of this.videos){
|
||||||
if (vd.isPlaying()) {
|
if (vd.isPlaying()) {
|
||||||
vd.restoreAr();
|
vd.restoreAr();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for(var vd of this.videos){
|
for(let vd of this.videos){
|
||||||
vd.restoreAr();
|
vd.restoreAr();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setStretchMode(stretchMode, playingOnly, fixedStretchRatio){
|
setStretchMode(stretchMode, playingOnly?: boolean, fixedStretchRatio?: boolean){
|
||||||
// TODO: find a way to only change aspect ratio for one video
|
// TODO: find a way to only change aspect ratio for one video
|
||||||
|
|
||||||
if (playingOnly) {
|
if (playingOnly) {
|
||||||
for(var vd of this.videos){
|
for(let vd of this.videos){
|
||||||
if (vd.isPlaying()) {
|
if (vd.isPlaying()) {
|
||||||
vd.setStretchMode(stretchMode, fixedStretchRatio)
|
vd.setStretchMode(stretchMode, fixedStretchRatio)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for(var vd of this.videos){
|
for(let vd of this.videos){
|
||||||
vd.setStretchMode(stretchMode, fixedStretchRatio)
|
vd.setStretchMode(stretchMode, fixedStretchRatio)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setZoom(zoomLevel, no_announce, playingOnly) {
|
setZoom(zoomLevel, no_announce?: boolean, playingOnly?: boolean) {
|
||||||
if (playingOnly) {
|
if (playingOnly) {
|
||||||
for(var vd of this.videos) {
|
for(let vd of this.videos) {
|
||||||
if (vd.isPlaying()) {
|
if (vd.isPlaying()) {
|
||||||
vd.setZoom(zoomLevel, no_announce);
|
vd.setZoom(zoomLevel, no_announce);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for(var vd of this.videos) {
|
for(let vd of this.videos) {
|
||||||
vd.setZoom(zoomLevel, no_announce);
|
vd.setZoom(zoomLevel, no_announce);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
zoomStep(step, playingOnly) {
|
zoomStep(step, playingOnly?: boolean) {
|
||||||
for(var vd of this.videos){
|
for(let vd of this.videos){
|
||||||
if (!playingOnly || vd.isPlaying()) {
|
if (!playingOnly || vd.isPlaying()) {
|
||||||
vd.zoomStep(step);
|
vd.zoomStep(step);
|
||||||
}
|
}
|
||||||
@ -530,19 +558,19 @@ class PageInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
markPlayer(name, color) {
|
markPlayer(name, color) {
|
||||||
for (var vd of this.videos) {
|
for (let vd of this.videos) {
|
||||||
vd.markPlayer(name,color);
|
vd.markPlayer(name,color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unmarkPlayer() {
|
unmarkPlayer() {
|
||||||
for (var vd of this.videos) {
|
for (let vd of this.videos) {
|
||||||
vd.unmarkPlayer();
|
vd.unmarkPlayer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
announceZoom(scale) {
|
announceZoom(scale) {
|
||||||
if (this.announceZoomTimeout) {
|
if (this.announceZoomTimeout) {
|
||||||
clearTimeout(this.announceZoom);
|
clearTimeout(this.announceZoomTimeout);
|
||||||
}
|
}
|
||||||
this.currentZoomScale = scale;
|
this.currentZoomScale = scale;
|
||||||
const ths = this;
|
const ths = this;
|
||||||
@ -550,13 +578,13 @@ class PageInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setManualTick(manualTick) {
|
setManualTick(manualTick) {
|
||||||
for(var vd of this.videos) {
|
for(let vd of this.videos) {
|
||||||
vd.setManualTick();
|
vd.setManualTick(manualTick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tick() {
|
tick() {
|
||||||
for(var vd of this.videos) {
|
for(let vd of this.videos) {
|
||||||
vd.tick();
|
vd.tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,503 +0,0 @@
|
|||||||
import Debug from '../../conf/Debug';
|
|
||||||
import ExtensionMode from '../../../common/enums/extension-mode.enum'
|
|
||||||
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
|
|
||||||
import PlayerNotificationUi from '../uwui/PlayerNotificationUI';
|
|
||||||
import PlayerUi from '../uwui/PlayerUI';
|
|
||||||
|
|
||||||
if (process.env.CHANNEL !== 'stable'){
|
|
||||||
console.info("Loading: PlayerData.js");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* sprejme <video> tag (element) in seznam imen, ki se lahko pojavijo v razredih oz. id staršev.
|
|
||||||
// vrne dimenzije predvajalnika (širina, višina)
|
|
||||||
//
|
|
||||||
// Na youtube v theater mode je razširitev rahlo pokvarjena. Video tag ostane večji od predvajalnika, ko se zapusti
|
|
||||||
// celozaslonski način. Ta funkcija skuša to težavo rešiti tako, da poišče element predvajalnika, ki je zavit okoli videa.
|
|
||||||
//
|
|
||||||
// Funkcija izkorišča lastnost, da bi načeloma moral biti vsak zunanji element večji od notranjega. Najmanjši element od
|
|
||||||
// <video> značke pa do korena drevesa bi tako moral biti predvajalnik.
|
|
||||||
//
|
|
||||||
// Če je podan seznam imen, potem funkcija vrne dimenzije prvega elementa, ki v id oz. razredu vsebuje katerokoli ime iz seznama
|
|
||||||
//
|
|
||||||
// | EN |
|
|
||||||
//
|
|
||||||
// accepts <video> tag (element) and list of names that can appear in id or class
|
|
||||||
// returns player dimensions (width, height)
|
|
||||||
//
|
|
||||||
// Theater mode is mildly broken on youtube. <video> tag remains bigger than the player after leaving the fullscreen mode, and
|
|
||||||
// there's nothing we can do about that. This function aims to solve the problem by finding the player element that's wrapped around
|
|
||||||
// the <video> tag.
|
|
||||||
//
|
|
||||||
// In general, an outer tag should be bigger than the inner tag. Therefore the smallest element between <video> tag and the document
|
|
||||||
// root should be the player.
|
|
||||||
//
|
|
||||||
// If list of names is provided, the function returns dimensions of the first element that contains any name from the list in either
|
|
||||||
// id or class.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class PlayerData {
|
|
||||||
constructor(videoData) {
|
|
||||||
try {
|
|
||||||
this.logger = videoData.logger;
|
|
||||||
this.videoData = videoData;
|
|
||||||
this.video = videoData.video;
|
|
||||||
this.settings = videoData.settings;
|
|
||||||
this.extensionMode = videoData.extensionMode;
|
|
||||||
this.invalid = false;
|
|
||||||
this.element = this.getPlayer();
|
|
||||||
|
|
||||||
this.notificationService = new PlayerNotificationUi(this.element, this.settings);
|
|
||||||
|
|
||||||
this.ui = new PlayerUi(this.element, this.settings);
|
|
||||||
this.dimensions = undefined;
|
|
||||||
this.overlayNode = undefined;
|
|
||||||
|
|
||||||
this.periodicallyRefreshPlayerElement = false;
|
|
||||||
try {
|
|
||||||
this.periodicallyRefreshPlayerElement = this.settings.active.sites[window.location.hostname].DOM.player.periodicallyRefreshPlayerElement;
|
|
||||||
} catch (e) {
|
|
||||||
// no biggie — that means we don't have any special settings for this site.
|
|
||||||
}
|
|
||||||
|
|
||||||
// this happens when we don't find a matching player element
|
|
||||||
if (!this.element) {
|
|
||||||
this.invalid = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.extensionMode === ExtensionMode.Enabled) {
|
|
||||||
this.checkPlayerSizeChange();
|
|
||||||
}
|
|
||||||
this.startChangeDetection();
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
console.error('[Ultrawidify::PlayerData::ctor] There was an error setting up player data. You should be never seeing this message. Error:', e);
|
|
||||||
this.invalid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async sleep(timeout) {
|
|
||||||
return new Promise( (resolve, reject) => setTimeout(() => resolve(), timeout));
|
|
||||||
}
|
|
||||||
|
|
||||||
static isFullScreen(){
|
|
||||||
return ( window.innerHeight == window.screen.height && window.innerWidth == window.screen.width);
|
|
||||||
}
|
|
||||||
|
|
||||||
// player size observer may not be strictly necessary here
|
|
||||||
onPlayerDimensionsChanged(mutationList, observer, context) {
|
|
||||||
if (context.checkPlayerSizeChange()) {
|
|
||||||
context.videoData.resizer.restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
start(){
|
|
||||||
this.startChangeDetection();
|
|
||||||
}
|
|
||||||
|
|
||||||
stop(){
|
|
||||||
this.halted = true;
|
|
||||||
this.stopChangeDetection();
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
this.stopChangeDetection();
|
|
||||||
this.destroyOverlay();
|
|
||||||
this.notificationService?.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
startChangeDetection(){
|
|
||||||
if (this.invalid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const ths = this;
|
|
||||||
this.observer = new MutationObserver((m,o) => this.onPlayerDimensionsChanged(m,o,ths));
|
|
||||||
|
|
||||||
const observerConf = {
|
|
||||||
attributes: true,
|
|
||||||
// attributeFilter: ['style', 'class'],
|
|
||||||
attributeOldValue: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.observer.observe(this.element, observerConf);
|
|
||||||
} catch (e) {
|
|
||||||
console.error("failed to set observer",e )
|
|
||||||
}
|
|
||||||
// legacy mode still exists, but acts as a fallback for observers and is triggered less
|
|
||||||
// frequently in order to avoid too many pointless checks
|
|
||||||
this.legacyChangeDetection();
|
|
||||||
}
|
|
||||||
|
|
||||||
async legacyChangeDetection() {
|
|
||||||
while (!this.halted) {
|
|
||||||
await this.sleep(1000);
|
|
||||||
try {
|
|
||||||
this.doPeriodicPlayerElementChangeCheck();
|
|
||||||
} catch (e) {
|
|
||||||
console.error('[PlayerData::legacycd] this message is pretty high on the list of messages you shouldnt see', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
doPeriodicPlayerElementChangeCheck() {
|
|
||||||
if (this.periodicallyRefreshPlayerElement) {
|
|
||||||
this.forceDetectPlayerElementChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stopChangeDetection(){
|
|
||||||
this.observer.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
makeOverlay() {
|
|
||||||
if (!this.overlayNode) {
|
|
||||||
this.destroyOverlay();
|
|
||||||
}
|
|
||||||
|
|
||||||
var overlay = document.createElement('div');
|
|
||||||
overlay.style.width = '100%';
|
|
||||||
overlay.style.height = '100%';
|
|
||||||
overlay.style.position = 'absolute';
|
|
||||||
overlay.style.top = '0';
|
|
||||||
overlay.style.left = '0';
|
|
||||||
overlay.style.zIndex = '1000000000';
|
|
||||||
overlay.style.pointerEvents = 'none';
|
|
||||||
|
|
||||||
this.overlayNode = overlay;
|
|
||||||
this.element.appendChild(overlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
destroyOverlay() {
|
|
||||||
if(this.playerIdElement) {
|
|
||||||
this.playerIdElement.remove();
|
|
||||||
this.playerIdElement = undefined;
|
|
||||||
}
|
|
||||||
if (this.overlayNode) {
|
|
||||||
this.overlayNode.remove();
|
|
||||||
this.overlayNode = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
markPlayer(name, color) {
|
|
||||||
if (!this.overlayNode) {
|
|
||||||
this.makeOverlay();
|
|
||||||
}
|
|
||||||
if (this.playerIdElement) {
|
|
||||||
this.playerIdElement.remove();
|
|
||||||
}
|
|
||||||
this.playerIdElement = document.createElement('div');
|
|
||||||
this.playerIdElement.innerHTML = `<div style="background-color: ${color}; color: #fff; position: absolute; top: 0; left: 0">${name}</div>`;
|
|
||||||
|
|
||||||
this.overlayNode.appendChild(this.playerIdElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
unmarkPlayer() {
|
|
||||||
this.logger.log('info', 'debug', "[PlayerData::unmarkPlayer] unmarking player!", {playerIdElement: this.playerIdElement});
|
|
||||||
if (this.playerIdElement) {
|
|
||||||
this.playerIdElement.innerHTML = '';
|
|
||||||
this.playerIdElement.remove();
|
|
||||||
}
|
|
||||||
this.playerIdElement = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
collectionHas(collection, element) {
|
|
||||||
for (let i = 0, len = collection.length; i < len; i++) {
|
|
||||||
if (collection[i] == element) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePlayerDimensions(element) {
|
|
||||||
const isFullScreen = PlayerData.isFullScreen();
|
|
||||||
|
|
||||||
if (element.offsetWidth !== this.dimensions?.width
|
|
||||||
|| element.offsetHeight !== this.dimensions?.height
|
|
||||||
|| isFullScreen !== this.dimensions?.fullscreen) {
|
|
||||||
|
|
||||||
// update dimensions only if they've changed, _before_ we do a restore (not after)
|
|
||||||
this.dimensions = {
|
|
||||||
width: element.offsetWidth,
|
|
||||||
height: element.offsetHeight,
|
|
||||||
fullscreen: isFullScreen
|
|
||||||
};
|
|
||||||
|
|
||||||
// actually re-calculate zoom when player size changes, but only if videoData.resizer
|
|
||||||
// is defined. Since resizer needs a PlayerData object to exist, videoData.resizer will
|
|
||||||
// be undefined the first time this function will run.
|
|
||||||
this.videoData.resizer?.restore();
|
|
||||||
|
|
||||||
// NOTE: it's possible that notificationService hasn't been initialized yet at this point.
|
|
||||||
// no biggie if it wasn't, we just won't replace the notification UI
|
|
||||||
this.notificationService?.replace(this.element);
|
|
||||||
this.reportPlayerDimensionForDebugging();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reportPlayerDimensionForDebugging() {
|
|
||||||
this.ui?.updateDebugInfo('player', {dimensions: this.dimensions, elementId: this.element.id, elementClasses: this.element.classList});
|
|
||||||
}
|
|
||||||
|
|
||||||
getPlayer() {
|
|
||||||
const host = window.location.hostname;
|
|
||||||
let element = this.video.parentNode;
|
|
||||||
const videoWidth = this.video.offsetWidth;
|
|
||||||
const videoHeight = this.video.offsetHeight;
|
|
||||||
const elementQ = [];
|
|
||||||
let scorePenalty = 0;
|
|
||||||
let score;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if(! element ){
|
|
||||||
this.logger.log('info', 'debug', "[PlayerDetect::_pd_getPlayer] element is not valid, doing nothing.", element)
|
|
||||||
if(this.element) {
|
|
||||||
const ths = this;
|
|
||||||
}
|
|
||||||
this.element = undefined;
|
|
||||||
this.dimensions = undefined;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// log the entire hierarchy from <video> to root
|
|
||||||
if (this.logger.canLog('playerDetect')) {
|
|
||||||
const logObj = [];
|
|
||||||
logObj.push(`window size: ${window.innerWidth} x ${window.innerHeight}`);
|
|
||||||
let e = element;
|
|
||||||
while (e) {
|
|
||||||
logObj.push({offsetSize: {width: e.offsetWidth, height: e.offsetHeight}, clientSize: {width: e.clientWidth, height: e.clientHeight}, element: e});
|
|
||||||
e = e.parentNode;
|
|
||||||
}
|
|
||||||
this.logger.log('info', 'playerDetect', "\n\n[PlayerDetect::getPlayer()] element hierarchy (video->root)", logObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.settings.active.sites[host]?.DOM?.player?.manual) {
|
|
||||||
if (this.settings.active.sites[host]?.DOM?.player?.useRelativeAncestor
|
|
||||||
&& this.settings.active.sites[host]?.DOM?.player?.videoAncestor) {
|
|
||||||
|
|
||||||
let parentsLeft = this.settings.active.sites[host].DOM.player.videoAncestor - 1;
|
|
||||||
while (parentsLeft --> 0) {
|
|
||||||
element = element.parentNode;
|
|
||||||
}
|
|
||||||
if (element) {
|
|
||||||
this.updatePlayerDimensions(element);
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
} else if (this.settings.active.sites[host]?.DOM?.player?.querySelectors) {
|
|
||||||
const allSelectors = document.querySelectorAll(this.settings.active.sites[host].DOM.player.querySelectors);
|
|
||||||
// actually we'll also score this branch in a similar way we score the regular, auto branch
|
|
||||||
while (element) {
|
|
||||||
|
|
||||||
// Let's see how this works
|
|
||||||
if (this.collectionHas(allSelectors, element)) {
|
|
||||||
score = 100; // every matching element gets a baseline 100 points
|
|
||||||
|
|
||||||
// elements that match the size get a hefty bonus
|
|
||||||
if ( (element.offsetWidth >= videoWidth && this.equalish(element.offsetHeight, videoHeight, 2))
|
|
||||||
|| (element.offsetHeight >= videoHeight && this.equalish(element.offsetWidth, videoHeight, 2))) {
|
|
||||||
score += 75;
|
|
||||||
}
|
|
||||||
|
|
||||||
// elements farther away from the video get a penalty
|
|
||||||
score -= (scorePenalty++) * 20;
|
|
||||||
|
|
||||||
// push the element on the queue/stack:
|
|
||||||
elementQ.push({
|
|
||||||
score: score,
|
|
||||||
element: element,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
element = element.parentNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// log player candidates
|
|
||||||
this.logger.log('info', 'playerDetect', 'player detect via query selector: element queue and final element:', {queue: elementQ, bestCandidate: elementQ.length ? elementQ.sort( (a,b) => b.score - a.score)[0].element : 'n/a'});
|
|
||||||
|
|
||||||
if (elementQ.length) {
|
|
||||||
// return element with biggest score
|
|
||||||
// if video player has not been found, proceed to automatic detection
|
|
||||||
const playerElement = elementQ.sort( (a,b) => b.score - a.score)[0].element;
|
|
||||||
this.updatePlayerDimensions(playerElement);
|
|
||||||
return playerElement;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to find element the old fashioned way
|
|
||||||
|
|
||||||
while (element){
|
|
||||||
// odstranimo čudne elemente, ti bi pokvarili zadeve
|
|
||||||
// remove weird elements, those would break our stuff
|
|
||||||
if ( element.offsetWidth == 0 || element.offsetHeight == 0){
|
|
||||||
element = element.parentNode;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// element je player, če je ena stranica enako velika kot video, druga pa večja ali enaka.
|
|
||||||
// za enakost dovolimo mala odstopanja
|
|
||||||
// element is player, if one of the sides is as long as the video and the other bigger (or same)
|
|
||||||
// we allow for tiny variations when checking for equality
|
|
||||||
if ( (element.offsetWidth >= videoWidth && this.equalish(element.offsetHeight, videoHeight, 2))
|
|
||||||
|| (element.offsetHeight >= videoHeight && this.equalish(element.offsetWidth, videoHeight, 2))) {
|
|
||||||
|
|
||||||
// todo — in case the match is only equalish and not exact, take difference into account when
|
|
||||||
// calculating score
|
|
||||||
|
|
||||||
score = 100;
|
|
||||||
|
|
||||||
// This entire section is disabled because of some bullshit on vk and some shady CIS streaming sites.
|
|
||||||
// Possibly removal of this criteria is not necessary, because there was also a bug with force player
|
|
||||||
//
|
|
||||||
|
|
||||||
// if (element.id.indexOf('player') !== -1) { // prefer elements with 'player' in id
|
|
||||||
// score += 75;
|
|
||||||
// }
|
|
||||||
// this has only been observed on steam
|
|
||||||
// if (element.id.indexOf('movie') !== -1) {
|
|
||||||
// score += 75;
|
|
||||||
// }
|
|
||||||
// if (element.classList.toString().indexOf('player') !== -1) { // prefer elements with 'player' in classlist, but a bit less than id
|
|
||||||
// score += 50;
|
|
||||||
// }
|
|
||||||
score -= scorePenalty++; // prefer elements closer to <video>
|
|
||||||
|
|
||||||
elementQ.push({
|
|
||||||
element: element,
|
|
||||||
score: score,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
element = element.parentNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// log player candidates
|
|
||||||
this.logger.log('info', 'playerDetect', 'player detect, auto/fallback: element queue and final element:', {queue: elementQ, bestCandidate: elementQ.length ? elementQ.sort( (a,b) => b.score - a.score)[0].element : 'n/a'});
|
|
||||||
|
|
||||||
if (elementQ.length) {
|
|
||||||
// return element with biggest score
|
|
||||||
const playerElement = elementQ.sort( (a,b) => b.score - a.score)[0].element;
|
|
||||||
|
|
||||||
this.updatePlayerDimensions(playerElement);
|
|
||||||
return playerElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if no candidates were found, something is obviously very, _very_ wrong.
|
|
||||||
// we return nothing. Player will be marked as invalid and setup will stop.
|
|
||||||
// VideoData should check for that before starting anything.
|
|
||||||
this.logger.log('warn', 'debug', '[PlayerData::getPlayer] no matching player was found for video', this.video, 'Extension cannot work on this site.');
|
|
||||||
return;
|
|
||||||
} catch (e) {
|
|
||||||
this.logger.log('crit', 'debug', '[PlayerData::getPlayer] something went wrong while detecting player:', e, 'Shutting down extension for this page');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
equalish(a,b, tolerance) {
|
|
||||||
return a > b - tolerance && a < b + tolerance;
|
|
||||||
}
|
|
||||||
|
|
||||||
forceDetectPlayerElementChange() {
|
|
||||||
// Player dimension changes get calculated every time updatePlayerDimensions is called (which happens
|
|
||||||
// every time getPlayer() detects an element). If updatePlayerDimension detects dimensions were changed,
|
|
||||||
// it will always re-apply current crop, rendering this function little more than a fancy alias for
|
|
||||||
// getPlayer().
|
|
||||||
this.getPlayer();
|
|
||||||
}
|
|
||||||
|
|
||||||
forceRefreshPlayerElement() {
|
|
||||||
this.getPlayer();
|
|
||||||
}
|
|
||||||
|
|
||||||
checkPlayerSizeChange() {
|
|
||||||
// this 'if' is just here for debugging — real code starts later. It's safe to collapse and
|
|
||||||
// ignore the contents of this if (unless we need to change how logging works)
|
|
||||||
if (this.logger.canLog('debug')){
|
|
||||||
if (this.dimensions?.fullscreen){
|
|
||||||
if(! PlayerData.isFullScreen()){
|
|
||||||
this.logger.log('info', 'debug', "[PlayerDetect] player size changed. reason: exited fullscreen");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(! this.element) {
|
|
||||||
this.logger.log('info', 'playerDetect', "[PlayerDetect] player element isn't defined");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( this.element &&
|
|
||||||
( +this.dimensions?.width != +this.element?.offsetWidth ||
|
|
||||||
+this.dimensions?.height != +this.element?.offsetHeight )
|
|
||||||
) {
|
|
||||||
this.logger.log('info', 'debug', "[PlayerDetect] player size changed. reason: dimension change. Old dimensions?", this.dimensions?.width, this.dimensions?.height, "new dimensions:", this.element?.offsetWidth, this.element?.offsetHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if size doesn't match, update & return true
|
|
||||||
if (this.dimensions?.width != this.element.offsetWidth
|
|
||||||
|| this.dimensions?.height != this.element.offsetHeight ){
|
|
||||||
|
|
||||||
const isFullScreen = PlayerData.isFullScreen();
|
|
||||||
|
|
||||||
if (isFullScreen) {
|
|
||||||
this.dimensions = {
|
|
||||||
width: window.innerWidth,
|
|
||||||
height: window.innerHeight,
|
|
||||||
fullscreen: true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.dimensions = {
|
|
||||||
width: this.element.offsetWidth,
|
|
||||||
height: this.element.offsetHeight,
|
|
||||||
fullscreen: isFullScreen
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
checkFullscreenChange() {
|
|
||||||
const isFs = PlayerData.isFullScreen();
|
|
||||||
|
|
||||||
if (this.dimensions) {
|
|
||||||
if (this.dimensions.fullscreen != isFs) {
|
|
||||||
this.dimensions = {
|
|
||||||
fullscreen: isFs,
|
|
||||||
width: isFs ? screen.width : this.video.offsetWidth,
|
|
||||||
height: isFs ? screen.height : this.video.offsetHeight
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.log('info', 'debug', "[PlayerData::checkFullscreenChange] this.dimensions is not defined. Assuming fs change happened and setting default values.")
|
|
||||||
|
|
||||||
this.dimensions = {
|
|
||||||
fullscreen: isFs,
|
|
||||||
width: isFs ? screen.width : this.video.offsetWidth,
|
|
||||||
height: isFs ? screen.height : this.video.offsetHeight
|
|
||||||
};
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
showNotification(notificationId) {
|
|
||||||
this.notificationService?.showNotification(notificationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* NOTE: this method needs to be deleted once Edge gets its shit together.
|
|
||||||
*/
|
|
||||||
showEdgeNotification() {
|
|
||||||
// if (BrowserDetect.isEdgeUA && !this.settings.active.mutedNotifications?.browserSpecific?.edge?.brokenDrm?.[window.hostname]) {
|
|
||||||
// this.ui = new PlayerUi(this.element, this.settings);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.CHANNEL !== 'stable'){
|
|
||||||
console.info("PlayerData loaded");
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PlayerData;
|
|
@ -2,10 +2,45 @@ import Debug from '../../conf/Debug';
|
|||||||
import PlayerData from './PlayerData';
|
import PlayerData from './PlayerData';
|
||||||
import Resizer from '../video-transform/Resizer';
|
import Resizer from '../video-transform/Resizer';
|
||||||
import ArDetector from '../ar-detect/ArDetector';
|
import ArDetector from '../ar-detect/ArDetector';
|
||||||
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
|
import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import BrowserDetect from '../../conf/BrowserDetect';
|
||||||
|
import Logger from '../Logger';
|
||||||
|
import Settings from '../Settings';
|
||||||
|
import PageInfo from './PageInfo';
|
||||||
|
import { sleep } from '../../../common/js/utils';
|
||||||
|
import { hasDrm } from '../ar-detect/DrmDetecor';
|
||||||
|
|
||||||
class VideoData {
|
class VideoData {
|
||||||
|
//#region flags
|
||||||
|
arSetupComplete: boolean = false;
|
||||||
|
destroyed: boolean = false;
|
||||||
|
invalid: boolean = false;
|
||||||
|
videoStatusOk: boolean = false;
|
||||||
|
videoLoaded: boolean = false;
|
||||||
|
videoDimensionsLoaded: boolean = false;
|
||||||
|
paused: boolean = false;
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region misc stuff
|
||||||
|
vdid: string;
|
||||||
|
video: any;
|
||||||
|
observer: MutationObserver;
|
||||||
|
extensionMode: any;
|
||||||
|
userCssClassName: string;
|
||||||
|
validationId: number;
|
||||||
|
dimensions: any;
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region helper objects
|
||||||
|
logger: Logger;
|
||||||
|
settings: Settings;
|
||||||
|
pageInfo: PageInfo;
|
||||||
|
player: PlayerData;
|
||||||
|
resizer: Resizer;
|
||||||
|
arDetector: ArDetector;
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
|
||||||
constructor(video, settings, pageInfo){
|
constructor(video, settings, pageInfo){
|
||||||
window.ultrawidify.addVideo(this);
|
window.ultrawidify.addVideo(this);
|
||||||
@ -24,6 +59,8 @@ class VideoData {
|
|||||||
this.videoLoaded = false;
|
this.videoLoaded = false;
|
||||||
this.videoDimensionsLoaded = true;
|
this.videoDimensionsLoaded = true;
|
||||||
|
|
||||||
|
this.validationId = null;
|
||||||
|
|
||||||
this.dimensions = {
|
this.dimensions = {
|
||||||
width: this.video.offsetWidth,
|
width: this.video.offsetWidth,
|
||||||
height: this.video.offsetHeight,
|
height: this.video.offsetHeight,
|
||||||
@ -34,7 +71,16 @@ class VideoData {
|
|||||||
|
|
||||||
async onVideoLoaded() {
|
async onVideoLoaded() {
|
||||||
if (!this.videoLoaded) {
|
if (!this.videoLoaded) {
|
||||||
if (!this.video.videoWidth || !this.video.videoHeight) {
|
|
||||||
|
/**
|
||||||
|
* video.readyState 101:
|
||||||
|
* 0 — no info. Can't play.
|
||||||
|
* 1 — we have metadata but nothing else
|
||||||
|
* 2 — we have data for current playback position, but not future <--- meaning current frame, meaning Aard can work here or higher
|
||||||
|
* 3 — we have a lil bit for the future
|
||||||
|
* 4 — we'll survive to the end
|
||||||
|
*/
|
||||||
|
if (!this.video?.videoWidth || !this.video?.videoHeight || this.video.readyState < 2) {
|
||||||
return; // onVideoLoaded is a lie in this case
|
return; // onVideoLoaded is a lie in this case
|
||||||
}
|
}
|
||||||
this.logger.log('info', 'init', '%c[VideoData::onVideoLoaded] ——————————— Initiating phase two of videoData setup ———————————', 'color: #0f9');
|
this.logger.log('info', 'init', '%c[VideoData::onVideoLoaded] ——————————— Initiating phase two of videoData setup ———————————', 'color: #0f9');
|
||||||
@ -136,10 +182,37 @@ class VideoData {
|
|||||||
this.resizer = new Resizer(this);
|
this.resizer = new Resizer(this);
|
||||||
|
|
||||||
// INIT OBSERVERS
|
// INIT OBSERVERS
|
||||||
this.observer = new MutationObserver( (m, o) => {
|
try {
|
||||||
this.logger.log('info', 'debug', `[VideoData::setupStageTwo->mutationObserver] Mutation observer detected a mutation:`, {m, o});
|
if (BrowserDetect.firefox) {
|
||||||
this.onVideoDimensionsChanged(m, o, this)
|
this.observer = new MutationObserver(
|
||||||
});
|
_.debounce(
|
||||||
|
this.onVideoDimensionsChanged,
|
||||||
|
250,
|
||||||
|
{
|
||||||
|
leading: true,
|
||||||
|
trailing: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Chrome for some reason insists that this.onPlayerDimensionsChanged is not a function
|
||||||
|
// when it's not wrapped into an anonymous function
|
||||||
|
this.observer = new MutationObserver(
|
||||||
|
_.debounce(
|
||||||
|
(m, o) => {
|
||||||
|
this.onVideoDimensionsChanged(m, o)
|
||||||
|
},
|
||||||
|
250,
|
||||||
|
{
|
||||||
|
leading: true,
|
||||||
|
trailing: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[VideoData] Observer setup failed:', e);
|
||||||
|
}
|
||||||
this.observer.observe(this.video, observerConf);
|
this.observer.observe(this.video, observerConf);
|
||||||
|
|
||||||
// INIT AARD
|
// INIT AARD
|
||||||
@ -238,7 +311,7 @@ class VideoData {
|
|||||||
if (this.pageInfo.defaultCrop) {
|
if (this.pageInfo.defaultCrop) {
|
||||||
this.resizer.setAr(this.pageInfo.defaultCrop);
|
this.resizer.setAr(this.pageInfo.defaultCrop);
|
||||||
} else {
|
} else {
|
||||||
this.resizer.reset();
|
this.resizer.reset();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.stopArDetection();
|
this.stopArDetection();
|
||||||
@ -249,9 +322,15 @@ class VideoData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts fallback change detection (validates whether currently applied settings are correct)
|
||||||
|
*/
|
||||||
async fallbackChangeDetection() {
|
async fallbackChangeDetection() {
|
||||||
while (!this.destroyed && !this.invalid) {
|
const validationId = Date.now();
|
||||||
await this.sleep(500);
|
this.validationId = validationId;
|
||||||
|
|
||||||
|
while (!this.destroyed && !this.invalid && this.validationId === validationId) {
|
||||||
|
await sleep(500);
|
||||||
this.doPeriodicFallbackChangeDetectionCheck();
|
this.doPeriodicFallbackChangeDetectionCheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -260,14 +339,9 @@ class VideoData {
|
|||||||
this.validateVideoOffsets();
|
this.validateVideoOffsets();
|
||||||
}
|
}
|
||||||
|
|
||||||
async sleep(timeout) {
|
onVideoDimensionsChanged(mutationList, observer) {
|
||||||
return new Promise( (resolve) => setTimeout(() => resolve(), timeout));
|
if (!mutationList || this.video === undefined) { // something's wrong
|
||||||
}
|
if (observer && this.video) {
|
||||||
|
|
||||||
|
|
||||||
onVideoDimensionsChanged(mutationList, observer, context) {
|
|
||||||
if (!mutationList || context.video === undefined) { // something's wrong
|
|
||||||
if (observer && context.video) {
|
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -277,14 +351,14 @@ class VideoData {
|
|||||||
for (let mutation of mutationList) {
|
for (let mutation of mutationList) {
|
||||||
if (mutation.type === 'attributes') {
|
if (mutation.type === 'attributes') {
|
||||||
if (mutation.attributeName === 'class') {
|
if (mutation.attributeName === 'class') {
|
||||||
if(!context.video.classList.contains(this.userCssClassName) ) {
|
if(!this.video.classList.contains(this.userCssClassName) ) {
|
||||||
// force the page to include our class in classlist, if the classlist has been removed
|
// 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),
|
// 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
|
// 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).
|
// 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.
|
// This is a problem because INFINITE RECURSION TIME, and we _really_ don't want that.
|
||||||
context.video.classList.add(this.userCssClassName);
|
this.video.classList.add(this.userCssClassName);
|
||||||
context.video.classList.add('uw-ultrawidify-base-wide-screen');
|
this.video.classList.add('uw-ultrawidify-base-wide-screen');
|
||||||
}
|
}
|
||||||
// always trigger refresh on class changes, since change of classname might trigger change
|
// always trigger refresh on class changes, since change of classname might trigger change
|
||||||
// of the player size as well.
|
// of the player size as well.
|
||||||
@ -303,13 +377,13 @@ class VideoData {
|
|||||||
// adding player observer taught us that if element size gets triggered by a class, then
|
// adding player observer taught us that if element size gets triggered by a class, then
|
||||||
// the 'style' attributes don't necessarily trigger. This means we also need to trigger
|
// the 'style' attributes don't necessarily trigger. This means we also need to trigger
|
||||||
// restoreAr here, in case video size was changed this way
|
// restoreAr here, in case video size was changed this way
|
||||||
context.player.forceRefreshPlayerElement();
|
this.player.forceRefreshPlayerElement();
|
||||||
context.restoreAr();
|
this.restoreAr();
|
||||||
|
|
||||||
// sometimes something fucky wucky happens and mutations aren't detected correctly, so we
|
// sometimes something fucky wucky happens and mutations aren't detected correctly, so we
|
||||||
// try to get around that
|
// try to get around that
|
||||||
setTimeout( () => {
|
setTimeout( () => {
|
||||||
context.validateVideoOffsets();
|
this.validateVideoOffsets();
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,7 +418,7 @@ class VideoData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
// do nothing on fail
|
console.error('Validating video offsets failed:', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,7 +430,7 @@ class VideoData {
|
|||||||
* Gets the contents of the style attribute of the video element
|
* Gets the contents of the style attribute of the video element
|
||||||
* in a form of an object.
|
* in a form of an object.
|
||||||
*/
|
*/
|
||||||
getVideoStyle() {
|
getVideoStyle(): any {
|
||||||
// This will _always_ give us an array. Empty string gives an array
|
// This will _always_ give us an array. Empty string gives an array
|
||||||
// that contains one element. That element is an empty string.
|
// that contains one element. That element is an empty string.
|
||||||
const styleArray = (this.video.getAttribute('style') || '').split(';');
|
const styleArray = (this.video.getAttribute('style') || '').split(';');
|
||||||
@ -430,6 +504,11 @@ class VideoData {
|
|||||||
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
|
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasDrm(this.video)) {
|
||||||
|
this.player.showNotification('AARD_DRM');
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.arDetector) {
|
if (!this.arDetector) {
|
||||||
this.initArDetection();
|
this.initArDetection();
|
||||||
}
|
}
|
||||||
@ -467,7 +546,7 @@ class VideoData {
|
|||||||
}
|
}
|
||||||
this.paused = false;
|
this.paused = false;
|
||||||
try {
|
try {
|
||||||
this.resizer.start();
|
// this.resizer.start();
|
||||||
if (this.player) {
|
if (this.player) {
|
||||||
this.player.start();
|
this.player.start();
|
||||||
}
|
}
|
||||||
@ -502,12 +581,12 @@ class VideoData {
|
|||||||
this.resizer.setLastAr(lastAr);
|
this.resizer.setLastAr(lastAr);
|
||||||
}
|
}
|
||||||
|
|
||||||
setAr(ar, lastAr){
|
setAr(ar, lastAr?){
|
||||||
if (this.invalid) {
|
if (this.invalid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ar.type === AspectRatio.Fixed || ar.type === AspectRatio.FitHeight || ar.type === AspectRatio.FitHeight) {
|
if (ar.type === AspectRatioType.Fixed || ar.type === AspectRatioType.FitHeight || ar.type === AspectRatioType.FitHeight) {
|
||||||
this.player.forceRefreshPlayerElement();
|
this.player.forceRefreshPlayerElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,7 +607,7 @@ class VideoData {
|
|||||||
this.resizer.setLastAr('original');
|
this.resizer.setLastAr('original');
|
||||||
}
|
}
|
||||||
|
|
||||||
panHandler(event, forcePan) {
|
panHandler(event, forcePan?: boolean) {
|
||||||
if (this.invalid) {
|
if (this.invalid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
7
src/ext/lib/video-data/enums/RescanReason.enum.ts
Normal file
7
src/ext/lib/video-data/enums/RescanReason.enum.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
enum RescanReason {
|
||||||
|
PERIODIC = 0,
|
||||||
|
URL_CHANGE = 1,
|
||||||
|
MANUAL = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RescanReason;
|
@ -1,7 +0,0 @@
|
|||||||
var RescanReason = Object.freeze({
|
|
||||||
PERIODIC: 0,
|
|
||||||
URL_CHANGE: 1,
|
|
||||||
MANUAL: 2
|
|
||||||
});
|
|
||||||
|
|
||||||
export default RescanReason;
|
|
@ -24,7 +24,7 @@ class CssHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i in styleArray) {
|
for (let i in styleArray) {
|
||||||
styleArray[i] = styleArray[i].trim();
|
styleArray[i] = styleArray[i].trim();
|
||||||
// some sites do 'top: 50%; left: 50%; transform: <transform>' to center videos.
|
// some sites do 'top: 50%; left: 50%; transform: <transform>' to center videos.
|
||||||
// we dont wanna, because we already center videos on our own
|
// we dont wanna, because we already center videos on our own
|
||||||
@ -45,7 +45,7 @@ class CssHandler {
|
|||||||
static buildStyleString(styleArray) {
|
static buildStyleString(styleArray) {
|
||||||
let styleString = '';
|
let styleString = '';
|
||||||
|
|
||||||
for(var i in styleArray) {
|
for(let i in styleArray) {
|
||||||
if(styleArray[i]) {
|
if(styleArray[i]) {
|
||||||
styleString += styleArray[i] + "; ";
|
styleString += styleArray[i] + "; ";
|
||||||
}
|
}
|
@ -1,778 +0,0 @@
|
|||||||
import Debug from '../../conf/Debug';
|
|
||||||
import Scaler from './Scaler';
|
|
||||||
import Stretcher from './Stretcher';
|
|
||||||
import Zoom from './Zoom';
|
|
||||||
import PlayerData from '../video-data/PlayerData';
|
|
||||||
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';
|
|
||||||
import { sleep } from '../Util';
|
|
||||||
|
|
||||||
if(Debug.debug) {
|
|
||||||
console.log("Loading: Resizer.js");
|
|
||||||
}
|
|
||||||
|
|
||||||
class Resizer {
|
|
||||||
|
|
||||||
constructor(videoData) {
|
|
||||||
this.resizerId = (Math.random(99)*100).toFixed(0);
|
|
||||||
this.conf = videoData;
|
|
||||||
this.logger = videoData.logger;
|
|
||||||
this.video = videoData.video;
|
|
||||||
this.settings = videoData.settings;
|
|
||||||
this.extensionMode = videoData.extensionMode;
|
|
||||||
|
|
||||||
|
|
||||||
this.scaler = new Scaler(this.conf);
|
|
||||||
this.stretcher = new Stretcher(this.conf);
|
|
||||||
this.zoom = new Zoom(this.conf);
|
|
||||||
|
|
||||||
// load up default values
|
|
||||||
this.correctedVideoDimensions = {};
|
|
||||||
this.currentCss = {};
|
|
||||||
this.currentStyleString = "";
|
|
||||||
this.currentPlayerStyleString = "";
|
|
||||||
this.currentCssValidFor = {};
|
|
||||||
|
|
||||||
this.lastAr = {type: AspectRatio.Initial};
|
|
||||||
this.videoAlignment = this.settings.getDefaultVideoAlignment(window.location.hostname); // this is initial video alignment
|
|
||||||
this.destroyed = false;
|
|
||||||
|
|
||||||
|
|
||||||
if (this.settings.active.pan) {
|
|
||||||
this.canPan = this.settings.active.miscSettings.mousePan.enabled;
|
|
||||||
} else {
|
|
||||||
this.canPan = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.userCss = '';
|
|
||||||
this.userCssClassName = videoData.userCssClassName;
|
|
||||||
}
|
|
||||||
|
|
||||||
injectCss(css) {
|
|
||||||
this.conf.pageInfo.injectCss(css);
|
|
||||||
}
|
|
||||||
|
|
||||||
ejectCss(css) {
|
|
||||||
this.conf.pageInfo.ejectCss(css);
|
|
||||||
}
|
|
||||||
|
|
||||||
replaceCss(oldCss, newCss) {
|
|
||||||
this.conf.pageInfo.replaceCss(oldCss, newCss);
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareCss(css) {
|
|
||||||
return `.${this.userCssClassName} {${css}}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy(){
|
|
||||||
this.logger.log('info', ['debug', 'init'], `[Resizer::destroy] <rid:${this.resizerId}> received destroy command.`);
|
|
||||||
this.destroyed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateRatioForLegacyOptions(ar){
|
|
||||||
// also present as modeToAr in Scaler.js
|
|
||||||
if (ar.type !== AspectRatio.FitWidth && ar.type !== AspectRatio.FitHeight && ar.ratio) {
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
// Skrbi za "stare" možnosti, kot na primer "na širino zaslona", "na višino zaslona" in "ponastavi".
|
|
||||||
// Približevanje opuščeno.
|
|
||||||
// handles "legacy" options, such as 'fit to widht', 'fit to height' and AspectRatio.Reset. No zoom tho
|
|
||||||
var ratioOut;
|
|
||||||
|
|
||||||
if (!this.conf.video) {
|
|
||||||
this.logger.log('info', 'debug', "[Scaler.js::modeToAr] No video??",this.conf.video, "killing videoData");
|
|
||||||
this.conf.destroy();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (! this.conf.player.dimensions) {
|
|
||||||
ratioOut = screen.width / screen.height;
|
|
||||||
} else {
|
|
||||||
this.logger.log('info', 'debug', `[Resizer::calculateRatioForLegacyOptions] <rid:${this.resizerId}> Player dimensions:`, this.conf.player.dimensions.width ,'x', this.conf.player.dimensions.height,'aspect ratio:', this.conf.player.dimensions.width / this.conf.player.dimensions.height)
|
|
||||||
ratioOut = this.conf.player.dimensions.width / this.conf.player.dimensions.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
// POMEMBNO: lastAr je potrebno nastaviti šele po tem, ko kličemo _res_setAr(). _res_setAr() predvideva,
|
|
||||||
// da želimo nastaviti statično (type: 'static') razmerje stranic — tudi, če funkcijo kličemo tu oz. v ArDetect.
|
|
||||||
//
|
|
||||||
// IMPORTANT NOTE: lastAr needs to be set after _res_setAr() is called, as _res_setAr() assumes we're
|
|
||||||
// setting a static aspect ratio (even if the function is called from here or ArDetect).
|
|
||||||
|
|
||||||
var fileAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
|
|
||||||
|
|
||||||
if (ar.type === AspectRatio.FitWidth){
|
|
||||||
ar.ratio = ratioOut > fileAr ? ratioOut : fileAr;
|
|
||||||
}
|
|
||||||
else if(ar.type === AspectRatio.FitHeight){
|
|
||||||
ar.ratio = ratioOut < fileAr ? ratioOut : fileAr;
|
|
||||||
}
|
|
||||||
else if(ar.type === AspectRatio.Reset){
|
|
||||||
this.logger.log('info', 'debug', "[Scaler.js::modeToAr] Using original aspect ratio -", fileAr);
|
|
||||||
ar.ratio = fileAr;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
updateAr(ar) {
|
|
||||||
if (!ar) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some options require a bit more testing re: whether they make sense
|
|
||||||
// if they don't, we refuse to update aspect ratio until they do
|
|
||||||
if (ar.type === AspectRatio.Automatic || ar.type === AspectRatio.Fixed) {
|
|
||||||
if (!ar.ratio || isNaN(ar.ratio)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only update aspect ratio if there's a difference between the old and the new state
|
|
||||||
if (!this.lastAr || ar.type !== this.lastAr.type || ar.ratio !== this.lastAr.ratio) {
|
|
||||||
this.setAr(ar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async setAr(ar, lastAr) {
|
|
||||||
if (this.destroyed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.video.videoWidth || !this.video.videoHeight) {
|
|
||||||
this.logger.log('warning', 'debug', '[Resizer::setAr] <rid:'+this.resizerId+'> Video has no width or no height. This is not allowed. Aspect ratio will not be set, and videoData will be uninitialized.');
|
|
||||||
this.conf.videoUnloaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.log('info', 'debug', '[Resizer::setAr] <rid:'+this.resizerId+'> trying to set ar. New ar:', ar)
|
|
||||||
|
|
||||||
if (ar == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const siteSettings = this.settings.active.sites[window.location.hostname];
|
|
||||||
|
|
||||||
// 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 && ar.type !== AspectRatio.Manual) // anything not these two _always_ changes AR
|
|
||||||
|| ar.type !== this.lastAr.type // this also means aspect ratio has changed
|
|
||||||
|| ar.ratio !== this.lastAr.ratio // this also means aspect ratio has changed
|
|
||||||
) {
|
|
||||||
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
|
|
||||||
// if (siteSettings?.autoarPreventConditions?.videoStyleString) {
|
|
||||||
// const styleString = (this.video.getAttribute('style') || '').split(';');
|
|
||||||
|
|
||||||
// if (siteSettings.autoarPreventConditions.videoStyleString.containsProperty) {
|
|
||||||
// const bannedProperties = siteSettings.autoarPreventConditions.videoStyleString.containsProperty;
|
|
||||||
// for (const prop in bannedProperties) {
|
|
||||||
// for (const s of styleString) {
|
|
||||||
// if (s.trim().startsWith(prop)) {
|
|
||||||
|
|
||||||
// // check if css property has a list of allowed values:
|
|
||||||
// if (bannedProperties[prop].allowedValues) {
|
|
||||||
// const styleValue = s.split(':')[1].trim();
|
|
||||||
|
|
||||||
// // check if property value is on the list of allowed values
|
|
||||||
// // if it's not, we aren't allowed to start aard
|
|
||||||
// if (bannedProperties[prop].allowedValues.indexOf(styleValue) === -1) {
|
|
||||||
// this.logger.log('error', 'debug', "%c[Resizer::setAr] video style contains forbidden css property/value combo: ", "color: #900, background: #100", prop, " — we aren't allowed to start autoar.")
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// // no allowed values, no problem. We have forbidden property
|
|
||||||
// // and this means aard can't start.
|
|
||||||
// this.logger.log('info', 'debug', "%c[Resizer::setAr] video style contains forbidden css property: ", "color: #900, background: #100", prop, " — we aren't allowed to start autoar.")
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (lastAr) {
|
|
||||||
this.lastAr = this.calculateRatioForLegacyOptions(lastAr);
|
|
||||||
ar = this.calculateRatioForLegacyOptions(ar);
|
|
||||||
} else {
|
|
||||||
// NOTE: "fitw" "fith" and "reset" should ignore ar.ratio bit, but
|
|
||||||
// I'm not sure whether they do. Check that.
|
|
||||||
ar = this.calculateRatioForLegacyOptions(ar);
|
|
||||||
if (! ar) {
|
|
||||||
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}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (this.extensionMode === ExtensionMode.Basic && !PlayerData.isFullScreen() && ar.type !== AspectRatio.Reset) {
|
|
||||||
// // don't actually apply or calculate css when using basic mode if not in fullscreen
|
|
||||||
// // ... unless we're resetting the aspect ratio to original
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (! this.video) {
|
|
||||||
this.conf.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
// pause AR on:
|
|
||||||
// * ar.type NOT automatic
|
|
||||||
// * ar.type is auto, but stretch is set to basic basic stretch
|
|
||||||
//
|
|
||||||
// unpause when using other modes
|
|
||||||
if (ar.type !== AspectRatio.Automatic || this.stretcher.mode === Stretch.Basic) {
|
|
||||||
this.conf?.arDetector?.pause();
|
|
||||||
} else {
|
|
||||||
if (this.lastAr.type === AspectRatio.Automatic) {
|
|
||||||
this.conf?.arDetector?.unpause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// do stretch thingy
|
|
||||||
if (this.stretcher.mode === Stretch.NoStretch
|
|
||||||
|| this.stretcher.mode === Stretch.Conditional
|
|
||||||
|| this.stretcher.mode === Stretch.FixedSource){
|
|
||||||
|
|
||||||
var stretchFactors = this.scaler.calculateCrop(ar);
|
|
||||||
|
|
||||||
if(! stretchFactors || stretchFactors.error){
|
|
||||||
this.logger.log('error', 'debug', `[Resizer::setAr] <rid:${this.resizerId}> failed to set AR due to problem with calculating crop. Error:`, stretchFactors?.error);
|
|
||||||
if (stretchFactors?.error === 'no_video'){
|
|
||||||
this.conf.destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we could have issued calculate crop too early. Let's tell VideoData that there's something wrong
|
|
||||||
// and exit this function. When <video> will receive onloadeddata or ontimeupdate (receiving either
|
|
||||||
// of the two means video is loaded or playing, and that means video has proper dimensions), it will
|
|
||||||
// try to reset or re-apply aspect ratio when the video is finally ready.
|
|
||||||
if (stretchFactors?.error === 'illegal_video_dimensions') {
|
|
||||||
this.conf.videoDimensionsLoaded = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.stretcher.mode === Stretch.Conditional){
|
|
||||||
this.stretcher.applyConditionalStretch(stretchFactors, ar.ratio);
|
|
||||||
} else if (this.stretcher.mode === Stretch.FixedSource) {
|
|
||||||
this.stretcher.applyStretchFixedSource(stretchFactors);
|
|
||||||
}
|
|
||||||
this.logger.log('info', 'debug', "[Resizer::setAr] Processed stretch factors for ",
|
|
||||||
this.stretcher.mode === Stretch.NoStretch ? 'stretch-free crop.' :
|
|
||||||
this.stretcher.mode === Stretch.Conditional ? 'crop with conditional stretch.' : 'crop with fixed stretch',
|
|
||||||
'Stretch factors are:', stretchFactors
|
|
||||||
);
|
|
||||||
|
|
||||||
} else if (this.stretcher.mode === Stretch.Hybrid) {
|
|
||||||
var stretchFactors = this.stretcher.calculateStretch(ar.ratio);
|
|
||||||
this.logger.log('info', 'debug', '[Resizer::setAr] Processed stretch factors for hybrid stretch/crop. Stretch factors are:', stretchFactors);
|
|
||||||
} else if (this.stretcher.mode === Stretch.Fixed) {
|
|
||||||
var stretchFactors = this.stretchFactors.calculateStretchFixed(ar.ratio)
|
|
||||||
} else if (this.stretcher.mode === Stretch.Basic) {
|
|
||||||
var stretchFactors = this.stretcher.calculateBasicStretch();
|
|
||||||
this.logger.log('info', 'debug', '[Resizer::setAr] Processed stretch factors for basic stretch. Stretch factors are:', stretchFactors);
|
|
||||||
} else {
|
|
||||||
var stretchFactors = {xFactor: 1, yFactor: 1};
|
|
||||||
this.logger.log('error', 'debug', '[Resizer::setAr] Okay wtf happened? If you see this, something has gone wrong', stretchFactors,"\n------[ i n f o d u m p ]------\nstretcher:", this.stretcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
const debugObject = {
|
|
||||||
stretch: {
|
|
||||||
x: stretchFactors.xFactor,
|
|
||||||
y: stretchFactors.yFactor
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.zoom.applyZoom(stretchFactors);
|
|
||||||
|
|
||||||
debugObject['stretchAfterZoom'] = {
|
|
||||||
x: stretchFactors.xFactor,
|
|
||||||
y: stretchFactors.yFactor
|
|
||||||
}
|
|
||||||
|
|
||||||
var translate = this.computeOffsets(stretchFactors);
|
|
||||||
this.applyCss(stretchFactors, translate);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
toFixedAr() {
|
|
||||||
// converting to fixed AR means we also turn off autoAR
|
|
||||||
this.setAr({
|
|
||||||
ar: this.lastAr.ar,
|
|
||||||
type: AspectRatio.Fixed
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
resetLastAr() {
|
|
||||||
this.lastAr = {type: AspectRatio.Initial};
|
|
||||||
}
|
|
||||||
|
|
||||||
setLastAr(override){
|
|
||||||
this.lastAr = override;
|
|
||||||
}
|
|
||||||
|
|
||||||
getLastAr(){
|
|
||||||
return this.lastAr;
|
|
||||||
}
|
|
||||||
|
|
||||||
setStretchMode(stretchMode, fixedStretchRatio){
|
|
||||||
this.stretcher.setStretchMode(stretchMode, fixedStretchRatio);
|
|
||||||
this.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
panHandler(event, forcePan) {
|
|
||||||
if (this.canPan || forcePan) {
|
|
||||||
if(!this.conf.player || !this.conf.player.element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// dont allow weird floats
|
|
||||||
this.videoAlignment = VideoAlignment.Center;
|
|
||||||
|
|
||||||
// because non-fixed aspect ratios reset panning:
|
|
||||||
if (this.lastAr.type !== AspectRatio.Fixed) {
|
|
||||||
this.toFixedAr();
|
|
||||||
}
|
|
||||||
|
|
||||||
const player = this.conf.player.element;
|
|
||||||
|
|
||||||
const relativeX = (event.pageX - player.offsetLeft) / player.offsetWidth;
|
|
||||||
const relativeY = (event.pageY - player.offsetTop) / player.offsetHeight;
|
|
||||||
|
|
||||||
this.logger.log('info', 'mousemove', "[Resizer::panHandler] mousemove.pageX, pageY:", event.pageX, event.pageY, "\nrelativeX/Y:", relativeX, relativeY)
|
|
||||||
|
|
||||||
this.setPan(relativeX, relativeY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resetPan() {
|
|
||||||
this.pan = {};
|
|
||||||
this.videoAlignment = this.settings.getDefaultVideoAlignment(window.location.hostname);
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
if(! this.pan){
|
|
||||||
this.pan = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.settings.active.miscSettings.mousePanReverseMouse) {
|
|
||||||
this.pan.relativeOffsetX = (relativeMousePosX * 1.1) - 0.55;
|
|
||||||
this.pan.relativeOffsetY = (relativeMousePosY * 1.1) - 0.55;
|
|
||||||
} else {
|
|
||||||
this.pan.relativeOffsetX = -(relativeMousePosX * 1.1) + 0.55;
|
|
||||||
this.pan.relativeOffsetY = -(relativeMousePosY * 1.1) + 0.55;
|
|
||||||
}
|
|
||||||
this.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
setVideoAlignment(videoAlignment) {
|
|
||||||
this.videoAlignment = videoAlignment;
|
|
||||||
this.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
restore() {
|
|
||||||
this.logger.log('info', 'debug', "[Resizer::restore] <rid:"+this.resizerId+"> attempting to restore aspect ratio", {'lastAr': this.lastAr} );
|
|
||||||
|
|
||||||
// this is true until we verify that css has actually been applied
|
|
||||||
if(this.lastAr.type === AspectRatio.Initial){
|
|
||||||
this.setAr({type: AspectRatio.Reset});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (this.lastAr?.ratio === null) {
|
|
||||||
// if this is the case, we do nothing as we have the correct aspect ratio
|
|
||||||
// throw "Last ar is null!"
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.setAr(this.lastAr, this.lastAr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(){
|
|
||||||
this.setStretchMode(this.settings.active.sites[window.location.hostname]?.stretch ?? this.settings.active.sites['@global'].stretch);
|
|
||||||
this.zoom.setZoom(1);
|
|
||||||
this.resetPan();
|
|
||||||
this.setAr({type: AspectRatio.Reset});
|
|
||||||
}
|
|
||||||
|
|
||||||
setPanMode(mode) {
|
|
||||||
if (mode === 'enable') {
|
|
||||||
this.canPan = true;
|
|
||||||
} else if (mode === 'disable') {
|
|
||||||
this.canPan = false;
|
|
||||||
} else if (mode === 'toggle') {
|
|
||||||
this.canPan = !this.canPan;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resetPan(){
|
|
||||||
this.pan = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
setZoom(zoomLevel, no_announce) {
|
|
||||||
this.zoom.setZoom(zoomLevel, no_announce);
|
|
||||||
}
|
|
||||||
|
|
||||||
zoomStep(step){
|
|
||||||
this.zoom.zoomStep(step);
|
|
||||||
}
|
|
||||||
|
|
||||||
resetZoom(){
|
|
||||||
this.zoom.setZoom(1);
|
|
||||||
this.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
resetCrop(){
|
|
||||||
this.setAr({type: AspectRatio.Reset});
|
|
||||||
}
|
|
||||||
|
|
||||||
resetStretch(){
|
|
||||||
this.stretcher.setStretchMode(Stretch.NoStretch);
|
|
||||||
this.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// mostly internal stuff
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the size of the video file _as displayed_ on the screen.
|
|
||||||
* Consider the following example:
|
|
||||||
*
|
|
||||||
* * player dimensions are 2560x1080
|
|
||||||
* * <video> is child of player
|
|
||||||
* * <video> has the following css: {width: 100%, height: 100%}
|
|
||||||
* * video file dimensions are 1280x720
|
|
||||||
*
|
|
||||||
* CSS will ensure that the dimensions of <video> tag are equal to the dimension of the
|
|
||||||
* player element — that is, 2560x1080px. This is no bueno, because the browser will upscale
|
|
||||||
* the video file to take up as much space as it can (without stretching it). This means
|
|
||||||
* we'll get a 1920x1080 video (as displayed) and a letterbox.
|
|
||||||
*
|
|
||||||
* We can't get that number out of anywhere: video.videoWidth will return 1280 (video file
|
|
||||||
* dimensions) and .offsetWidth (and the likes) will return the <video> tag dimension. Neither
|
|
||||||
* will return the actual size of video as displayed, which we need in order to calculate the
|
|
||||||
* extra space to the left and right of the video.
|
|
||||||
*
|
|
||||||
* We make the assumption of the
|
|
||||||
*/
|
|
||||||
computeVideoDisplayedDimensions() {
|
|
||||||
const offsetWidth = this.conf.video.offsetWidth;
|
|
||||||
const offsetHeight = this.conf.video.offsetHeight;
|
|
||||||
|
|
||||||
const scaleX = offsetWidth / this.conf.video.videoWidth;
|
|
||||||
const scaleY = offsetHeight / this.conf.video.videoHeight;
|
|
||||||
|
|
||||||
// if differences between the scale factors are minimal, we presume offsetWidth and
|
|
||||||
// offsetHeight are the accurate enough for our needs
|
|
||||||
if (Math.abs(scaleX - scaleY) < 0.02) {
|
|
||||||
return {
|
|
||||||
realVideoWidth: offsetWidth,
|
|
||||||
realVideoHeight: offsetHeight,
|
|
||||||
marginX: 0,
|
|
||||||
marginY: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we're still here, we need to calculate real video dimensions
|
|
||||||
const diffX = Math.abs(scaleY * this.conf.video.videoWidth - offsetWidth);
|
|
||||||
const diffY = Math.abs(scaleX * this.conf.video.videoHeight - offsetHeight);
|
|
||||||
|
|
||||||
// in this case, we want to base our real dimensions off scaleX
|
|
||||||
// otherwise, we want to base it off scaleY
|
|
||||||
if (diffX < diffY) {
|
|
||||||
const realHeight = this.conf.video.videoHeight * scaleX;
|
|
||||||
return {
|
|
||||||
realVideoWidth: offsetWidth,
|
|
||||||
realVideoHeight: realHeight,
|
|
||||||
marginX: 0,
|
|
||||||
marginY: (offsetHeight - realHeight) * 0.5
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const realWidth = this.conf.video.videoWidth * scaleY;
|
|
||||||
return {
|
|
||||||
realVideoWidth: realWidth,
|
|
||||||
realVideoHeight: offsetHeight,
|
|
||||||
marginX: (offsetWidth - realWidth) * 0.5,
|
|
||||||
marginY: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
computeOffsets(stretchFactors){
|
|
||||||
this.logger.log('info', 'debug', "[Resizer::computeOffsets] <rid:"+this.resizerId+"> video will be aligned to ", this.settings.active.sites['@global'].videoAlignment);
|
|
||||||
|
|
||||||
const debugObject = {};
|
|
||||||
|
|
||||||
const {realVideoWidth, realVideoHeight, marginX, marginY} = this.computeVideoDisplayedDimensions();
|
|
||||||
|
|
||||||
debugObject['playerData'] = {
|
|
||||||
'dimensions': this.conf.player.dimensions,
|
|
||||||
'id': this.conf.player.element.id,
|
|
||||||
'classList': this.conf.player.element.classList
|
|
||||||
};
|
|
||||||
debugObject['videoRawData'] = {
|
|
||||||
streamDimensions: {
|
|
||||||
x: this.conf.video.videoWidth,
|
|
||||||
y: this.conf.video.videoHeight
|
|
||||||
},
|
|
||||||
displayedSize: {
|
|
||||||
x: realVideoWidth,
|
|
||||||
y: realVideoHeight
|
|
||||||
},
|
|
||||||
videoElementSize: {
|
|
||||||
x: this.conf.video.offsetWidth,
|
|
||||||
y: this.conf.video.offsetHeight
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const wdiff = this.conf.player.dimensions.width - realVideoWidth;
|
|
||||||
const hdiff = this.conf.player.dimensions.height - realVideoHeight;
|
|
||||||
|
|
||||||
if (wdiff < 0 && hdiff < 0 && this.zoom.scale > 1) {
|
|
||||||
this.conf.player.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
const wdiffAfterZoom = realVideoWidth * stretchFactors.xFactor - this.conf.player.dimensions.width;
|
|
||||||
const hdiffAfterZoom = realVideoHeight * stretchFactors.yFactor - this.conf.player.dimensions.height;
|
|
||||||
|
|
||||||
debugObject['transformedSize'] = {
|
|
||||||
x: realVideoWidth * stretchFactors.xFactor,
|
|
||||||
y: realVideoHeight * stretchFactors.yFactor
|
|
||||||
}
|
|
||||||
debugObject['sizeDifferenceToPlayer'] = {
|
|
||||||
beforeZoom: {
|
|
||||||
wdiff,
|
|
||||||
hdiff
|
|
||||||
},
|
|
||||||
afterZoom: {
|
|
||||||
wdiff: wdiffAfterZoom,
|
|
||||||
hdiff: hdiffAfterZoom,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const translate = {
|
|
||||||
x: wdiff * 0.5,
|
|
||||||
y: hdiff * 0.5,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.pan) {
|
|
||||||
// don't offset when video is smaller than player
|
|
||||||
if(wdiffAfterZoom >= 0 || hdiffAfterZoom >= 0) {
|
|
||||||
translate.x += wdiffAfterZoom * this.pan.relativeOffsetX * this.zoom.scale;
|
|
||||||
translate.y += hdiffAfterZoom * this.pan.relativeOffsetY * this.zoom.scale;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.videoAlignment == VideoAlignment.Left) {
|
|
||||||
translate.x += wdiffAfterZoom * 0.5;
|
|
||||||
}
|
|
||||||
else if (this.videoAlignment == VideoAlignment.Right) {
|
|
||||||
translate.x -= wdiffAfterZoom * 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debugObject['videoTransform'] = {
|
|
||||||
alignment: VideoAlignment.toString(this.videoAlignment),
|
|
||||||
panningEnabled: !!this.pan,
|
|
||||||
translate: {
|
|
||||||
x: translate.x,
|
|
||||||
y: translate.y,
|
|
||||||
},
|
|
||||||
scale: {
|
|
||||||
x: stretchFactors.xFactor,
|
|
||||||
y: stretchFactors.yFactor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.conf.player.reportPlayerDimensionForDebugging();
|
|
||||||
this.conf.player.ui?.updateDebugInfo('resizer', debugObject);
|
|
||||||
|
|
||||||
this.logger.log('info', ['debug', 'resizer'], "[Resizer::_res_computeOffsets] <rid:"+this.resizerId+"> calculated offsets:\n\n",
|
|
||||||
'---- data in ----',
|
|
||||||
'\nplayer dimensions: ', {w: this.conf.player.dimensions.width, h: this.conf.player.dimensions.height},
|
|
||||||
'\nvideo dimensions: ', {w: this.conf.video.offsetWidth, h: this.conf.video.offsetHeight},
|
|
||||||
'\nreal video dimensions:', {w: realVideoWidth, h: realVideoHeight},
|
|
||||||
'\nstretch factors: ', stretchFactors,
|
|
||||||
'\npan & zoom: ', this.pan, this.zoom.scale,
|
|
||||||
'\nwdiff, hdiff: ', wdiff, 'x', hdiff,
|
|
||||||
'\nwdiff, hdiffAfterZoom:', wdiffAfterZoom, 'x', hdiffAfterZoom,
|
|
||||||
'\n\n---- data out ----\n',
|
|
||||||
'translate:', translate);
|
|
||||||
|
|
||||||
// by the way, let's do a quick sanity check whether video player is doing any fuckies wuckies
|
|
||||||
// fucky wucky examples:
|
|
||||||
//
|
|
||||||
// * video width is bigger than player width AND video height is bigger than player height
|
|
||||||
// * video width is smaller than player width AND video height is smaller than player height
|
|
||||||
//
|
|
||||||
// In both examples, at most one of the two conditions can be true at the same time. If both
|
|
||||||
// conditions are true at the same time, we need to go 'chiny reckon' and recheck our player
|
|
||||||
// element. Chances are our video is not getting aligned correctly
|
|
||||||
if (
|
|
||||||
(this.conf.video.offsetWidth > this.conf.player.dimensions.width && this.conf.video.offsetHeight > this.conf.player.dimensions.height) ||
|
|
||||||
(this.conf.video.offsetWidth < this.conf.player.dimensions.width && this.conf.video.offsetHeight < this.conf.player.dimensions.height)
|
|
||||||
) {
|
|
||||||
this.logger.log('warn', ['debugger', 'resizer'], `[Resizer::_res_computeOffsets] <rid:${this.resizerId}> We are getting some incredibly funny results here.\n\n`,
|
|
||||||
`Video seems to be both wider and taller (or shorter and narrower) than player element at the same time. This is super duper not supposed to happen.\n\n`,
|
|
||||||
`Player element needs to be checked.`
|
|
||||||
)
|
|
||||||
if (this.conf.player.checkPlayerSizeChange()) {
|
|
||||||
this.conf.player.onPlayerDimensionsChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return translate;
|
|
||||||
}
|
|
||||||
|
|
||||||
buildStyleArray(existingStyleString, extraStyleString) {
|
|
||||||
if (existingStyleString) {
|
|
||||||
const styleArray = existingStyleString.split(";");
|
|
||||||
|
|
||||||
if (extraStyleString) {
|
|
||||||
const extraCss = extraStyleString.split(';');
|
|
||||||
let dup = false;
|
|
||||||
|
|
||||||
for (const ecss of extraCss) {
|
|
||||||
for (let i in styleArray) {
|
|
||||||
if (ecss.split(':')[0].trim() === styleArray[i].split(':')[0].trim()) {
|
|
||||||
dup = true;
|
|
||||||
styleArray[i] = ecss;
|
|
||||||
}
|
|
||||||
if (dup) {
|
|
||||||
dup = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
styleArray.push(ecss);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i in styleArray) {
|
|
||||||
styleArray[i] = styleArray[i].trim();
|
|
||||||
// some sites do 'top: 50%; left: 50%; transform: <transform>' to center videos.
|
|
||||||
// we dont wanna, because we already center videos on our own
|
|
||||||
if (styleArray[i].startsWith("transform:")
|
|
||||||
|| styleArray[i].startsWith("top:")
|
|
||||||
|| styleArray[i].startsWith("left:")
|
|
||||||
|| styleArray[i].startsWith("right:")
|
|
||||||
|| styleArray[i].startsWith("bottom:")
|
|
||||||
|| styleArray[i].startsWith("margin")
|
|
||||||
){
|
|
||||||
delete styleArray[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return styleArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
buildStyleString(styleArray) {
|
|
||||||
let styleString = '';
|
|
||||||
|
|
||||||
for(var i in styleArray) {
|
|
||||||
if(styleArray[i]) {
|
|
||||||
styleString += styleArray[i] + " !important; ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return styleString;
|
|
||||||
}
|
|
||||||
|
|
||||||
applyCss(stretchFactors, translate){
|
|
||||||
// apply extra CSS here. In case of duplicated properties, extraCss overrides
|
|
||||||
// default styleString
|
|
||||||
if (! this.video) {
|
|
||||||
this.logger.log('warn', 'debug', "[Resizer::applyCss] <rid:"+this.resizerId+"> Video went missing, doing nothing.");
|
|
||||||
|
|
||||||
this.conf.destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.log('info', ['debug', 'resizer'], "[Resizer::applyCss] <rid:"+this.resizerId+"> will apply css.", {stretchFactors, translate});
|
|
||||||
|
|
||||||
// save stuff for quick tests (before we turn numbers into css values):
|
|
||||||
this.currentVideoSettings = {
|
|
||||||
validFor: this.conf.player.dimensions,
|
|
||||||
// videoWidth: dimensions.width,
|
|
||||||
// videoHeight: dimensions.height
|
|
||||||
}
|
|
||||||
|
|
||||||
let extraStyleString;
|
|
||||||
try {
|
|
||||||
extraStyleString = this.settings.active.sites[window.location.hostname].DOM.video.additionalCss;
|
|
||||||
} catch (e) {
|
|
||||||
// do nothing. It's ok if no special settings are defined for this site, we'll just do defaults
|
|
||||||
}
|
|
||||||
|
|
||||||
const styleArray = this.buildStyleArray('', extraStyleString)
|
|
||||||
|
|
||||||
// add remaining elements
|
|
||||||
if (stretchFactors) {
|
|
||||||
styleArray.push(`transform: translate(${translate.x}px, ${translate.y}px) scale(${stretchFactors.xFactor}, ${stretchFactors.yFactor}) !important;`);
|
|
||||||
|
|
||||||
// important — guarantees video will be properly aligned
|
|
||||||
// Note that position:absolute cannot be put here, otherwise old.reddit /w RES breaks — videos embedded
|
|
||||||
// from certain hosts will get a height: 0px. This is bad.
|
|
||||||
styleArray.push("top: 0px !important; left: 0px !important; bottom: 0px !important; right: 0px;");
|
|
||||||
|
|
||||||
// important — some websites (cough reddit redesign cough) may impose some dumb max-width and max-height
|
|
||||||
// restrictions. If site has dumb shit like 'max-width: 100%' and 'max-height: 100vh' in their CSS, that
|
|
||||||
// shit will prevent us from applying desired crop. This means we need to tell websites to fuck off with
|
|
||||||
// that crap. We know better.
|
|
||||||
styleArray.push("max-width: none !important; max-height: none !important;");
|
|
||||||
}
|
|
||||||
const styleString = `${this.buildStyleString(styleArray)}${extraStyleString || ''}`; // string returned by buildStyleString() should end with ; anyway
|
|
||||||
|
|
||||||
// build style string back
|
|
||||||
this.setStyleString(styleString);
|
|
||||||
}
|
|
||||||
|
|
||||||
setStyleString (styleString) {
|
|
||||||
this.currentCssValidFor = this.conf.player.dimensions;
|
|
||||||
const newCssString = this.prepareCss(styleString);
|
|
||||||
|
|
||||||
// inject new CSS or replace existing one
|
|
||||||
if (!this.userCss) {
|
|
||||||
this.logger.log('info', ['debug', 'resizer'], "[Resizer::setStyleString] <rid:"+this.resizerId+"> Setting new css: ", newCssString);
|
|
||||||
|
|
||||||
this.injectCss(newCssString);
|
|
||||||
this.userCss = newCssString;
|
|
||||||
} else if (newCssString !== this.userCss) {
|
|
||||||
this.logger.log('info', ['debug', 'resizer'], "[Resizer::setStyleString] <rid:"+this.resizerId+"> Replacing css.\nOld string:", this.userCss, "\nNew string:", newCssString);
|
|
||||||
// we only replace css if it
|
|
||||||
this.replaceCss(this.userCss, newCssString);
|
|
||||||
this.userCss = newCssString;
|
|
||||||
} else {
|
|
||||||
this.logger.log('info', ['debug', 'resizer'], "[Resizer::setStyleString] <rid:"+this.resizerId+"> Existing css is still valid, doing nothing.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Resizer;
|
|
@ -1,12 +1,19 @@
|
|||||||
import Debug from '../../conf/Debug';
|
import Debug from '../../conf/Debug';
|
||||||
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
|
import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
|
||||||
|
import BrowserDetect from '../../conf/BrowserDetect';
|
||||||
|
import VideoData from '../video-data/VideoData';
|
||||||
|
import Logger from '../Logger';
|
||||||
|
|
||||||
|
|
||||||
// računa velikost videa za približevanje/oddaljevanje
|
// računa velikost videa za približevanje/oddaljevanje
|
||||||
// does video size calculations for zooming/cropping
|
// does video size calculations for zooming/cropping
|
||||||
|
|
||||||
|
|
||||||
class Scaler {
|
class Scaler {
|
||||||
// internal variables
|
//#region helper objects
|
||||||
|
conf: VideoData;
|
||||||
|
logger: Logger;
|
||||||
|
//#endregion
|
||||||
|
|
||||||
// functions
|
// functions
|
||||||
constructor(videoData) {
|
constructor(videoData) {
|
||||||
@ -17,13 +24,13 @@ class Scaler {
|
|||||||
|
|
||||||
// Skrbi za "stare" možnosti, kot na primer "na širino zaslona", "na višino zaslona" in "ponastavi".
|
// Skrbi za "stare" možnosti, kot na primer "na širino zaslona", "na višino zaslona" in "ponastavi".
|
||||||
// Približevanje opuščeno.
|
// Približevanje opuščeno.
|
||||||
// handles "legacy" options, such as 'fit to widht', 'fit to height' and AspectRatio.Reset. No zoom tho
|
// handles "legacy" options, such as 'fit to widht', 'fit to height' and AspectRatioType.Reset. No zoom tho
|
||||||
modeToAr (ar) {
|
modeToAr (ar) {
|
||||||
if (ar.type !== AspectRatio.FitWidth && ar.type !== AspectRatio.FitHeight && ar.ratio) {
|
if (ar.type !== AspectRatioType.FitWidth && ar.type !== AspectRatioType.FitHeight && ar.ratio) {
|
||||||
return ar.ratio;
|
return ar.ratio;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ratioOut;
|
let ratioOut;
|
||||||
|
|
||||||
if (!this.conf.video) {
|
if (!this.conf.video) {
|
||||||
this.logger.log('error', 'debug', "[Scaler.js::modeToAr] No video??",this.conf.video, "killing videoData");
|
this.logger.log('error', 'debug', "[Scaler.js::modeToAr] No video??",this.conf.video, "killing videoData");
|
||||||
@ -44,19 +51,19 @@ class Scaler {
|
|||||||
// IMPORTANT NOTE: lastAr needs to be set after _res_setAr() is called, as _res_setAr() assumes we're
|
// IMPORTANT NOTE: lastAr needs to be set after _res_setAr() is called, as _res_setAr() assumes we're
|
||||||
// setting a static aspect ratio (even if the function is called from here or ArDetect).
|
// setting a static aspect ratio (even if the function is called from here or ArDetect).
|
||||||
|
|
||||||
var fileAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
|
let fileAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
|
||||||
|
|
||||||
if (ar.type === AspectRatio.FitWidth) {
|
if (ar.type === AspectRatioType.FitWidth) {
|
||||||
ratioOut > fileAr ? ratioOut : fileAr
|
ratioOut > fileAr ? ratioOut : fileAr
|
||||||
ar.ratio = ratioOut;
|
ar.ratio = ratioOut;
|
||||||
return ratioOut;
|
return ratioOut;
|
||||||
}
|
}
|
||||||
else if (ar.type === AspectRatio.FitHeight) {
|
else if (ar.type === AspectRatioType.FitHeight) {
|
||||||
ratioOut < fileAr ? ratioOut : fileAr
|
ratioOut < fileAr ? ratioOut : fileAr
|
||||||
ar.ratio = ratioOut;
|
ar.ratio = ratioOut;
|
||||||
return ratioOut;
|
return ratioOut;
|
||||||
}
|
}
|
||||||
else if (ar.type === AspectRatio.Reset) {
|
else if (ar.type === AspectRatioType.Reset) {
|
||||||
this.logger.log('info', 'debug', "[Scaler.js::modeToAr] Using original aspect ratio -", fileAr)
|
this.logger.log('info', 'debug', "[Scaler.js::modeToAr] Using original aspect ratio -", fileAr)
|
||||||
ar.ar = fileAr;
|
ar.ar = fileAr;
|
||||||
return fileAr;
|
return fileAr;
|
||||||
@ -74,7 +81,7 @@ class Scaler {
|
|||||||
* undoes any zoom that style="height:123%" on the video element adds.
|
* undoes any zoom that style="height:123%" on the video element adds.
|
||||||
*
|
*
|
||||||
* There are few exceptions and additional caveatss:
|
* There are few exceptions and additional caveatss:
|
||||||
* * AspectRatio.FitHeight: we don't want to pre-downscale the video at all, as things
|
* * AspectRatioType.FitHeight: we don't want to pre-downscale the video at all, as things
|
||||||
* will be scaled to fit height as-is.
|
* will be scaled to fit height as-is.
|
||||||
* * When player is wider than stream, we want to undo any height compensations site
|
* * When player is wider than stream, we want to undo any height compensations site
|
||||||
* tacks on the video tag.
|
* tacks on the video tag.
|
||||||
@ -92,10 +99,10 @@ class Scaler {
|
|||||||
|
|
||||||
let arCorrectionFactor = 1;
|
let arCorrectionFactor = 1;
|
||||||
|
|
||||||
if (ar.type !== AspectRatio.FitHeight) {
|
if (ar.type !== AspectRatioType.FitHeight) {
|
||||||
if (playerAr < compensatedStreamAr) {
|
if (playerAr < compensatedStreamAr) {
|
||||||
arCorrectionFactor = this.conf.player.dimensions.width / this.conf.video.offsetWidth;
|
arCorrectionFactor = this.conf.player.dimensions.width / this.conf.video.offsetWidth;
|
||||||
} else if (ar.type !== AspectRatio.Reset) {
|
} else if (ar.type !== AspectRatioType.Reset) {
|
||||||
arCorrectionFactor /= heightCompensationFactor;
|
arCorrectionFactor /= heightCompensationFactor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,8 +121,8 @@ class Scaler {
|
|||||||
return {error: "illegal_video_dimensions"};
|
return {error: "illegal_video_dimensions"};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ar.type === AspectRatio.Reset){
|
if (ar.type === AspectRatioType.Reset){
|
||||||
return {xFactor: arCorrectionFactor, yFactor: arCorrectionFactor}
|
return {xFactor: arCorrectionFactor, yFactor: arCorrectionFactor, arCorrectionFactor: arCorrectionFactor}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle fuckie-wuckies
|
// handle fuckie-wuckies
|
||||||
@ -137,25 +144,40 @@ class Scaler {
|
|||||||
// Dejansko razmerje stranic datoteke/<video> značke
|
// Dejansko razmerje stranic datoteke/<video> značke
|
||||||
// Actual aspect ratio of the file/<video> tag
|
// Actual aspect ratio of the file/<video> tag
|
||||||
|
|
||||||
if (ar.type === AspectRatio.Initial || !ar.ratio) {
|
if (ar.type === AspectRatioType.Initial || !ar.ratio) {
|
||||||
ar.ratio = streamAr;
|
ar.ratio = streamAr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.logger.log('info', 'scaler', "[Scaler::calculateCrop] ar is " ,ar.ratio, ", file ar is", streamAr, ", this.conf.player.dimensions are ", this.conf.player.dimensions.width, "×", this.conf.player.dimensions.height, "| obj:", this.conf.player.dimensions);
|
this.logger.log('info', 'scaler', "[Scaler::calculateCrop] ar is " ,ar.ratio, ", file ar is", streamAr, ", this.conf.player.dimensions are ", this.conf.player.dimensions.width, "×", this.conf.player.dimensions.height, "| obj:", this.conf.player.dimensions);
|
||||||
|
|
||||||
var videoDimensions = {
|
const videoDimensions = {
|
||||||
xFactor: 1,
|
xFactor: 1,
|
||||||
yFactor: 1,
|
yFactor: 1,
|
||||||
actualWidth: 0, // width of the video (excluding pillarbox) when <video> tag height is equal to width
|
actualWidth: 0, // width of the video (excluding pillarbox) when <video> tag height is equal to width
|
||||||
actualHeight: 0, // height of the video (excluding letterbox) when <video> tag height is equal to height
|
actualHeight: 0, // height of the video (excluding letterbox) when <video> tag height is equal to height
|
||||||
|
arCorrectionFactor: arCorrectionFactor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.calculateCropCore(videoDimensions, ar.ratio, streamAr, playerAr)
|
||||||
|
|
||||||
|
return videoDimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The act of calculating aspect ratio is separated due to resue elsewhere in the extension.
|
||||||
|
* We are doing that to avoid surprise recursions.
|
||||||
|
* @param {*} videoDimensions
|
||||||
|
* @param {*} ar
|
||||||
|
* @param {*} streamAr
|
||||||
|
* @param {*} playerAr
|
||||||
|
*/
|
||||||
|
calculateCropCore(videoDimensions, ar, streamAr, playerAr) {
|
||||||
if (streamAr < playerAr) {
|
if (streamAr < playerAr) {
|
||||||
if (streamAr < ar.ratio){
|
if (streamAr < ar){
|
||||||
// in this situation we have to crop letterbox on top/bottom of the player
|
// in this situation we have to crop letterbox on top/bottom of the player
|
||||||
// we cut it, but never more than the player
|
// we cut it, but never more than the player
|
||||||
videoDimensions.xFactor = Math.min(ar.ratio, playerAr) / streamAr;
|
videoDimensions.xFactor = Math.min(ar, playerAr) / streamAr;
|
||||||
videoDimensions.yFactor = videoDimensions.xFactor;
|
videoDimensions.yFactor = videoDimensions.xFactor;
|
||||||
} else {
|
} else {
|
||||||
// in this situation, we would be cutting pillarbox. Inside horizontal player.
|
// in this situation, we would be cutting pillarbox. Inside horizontal player.
|
||||||
@ -164,7 +186,7 @@ class Scaler {
|
|||||||
videoDimensions.yFactor = 1;
|
videoDimensions.yFactor = 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (streamAr < ar.ratio || playerAr < ar.ratio){
|
if (streamAr < ar || playerAr < ar){
|
||||||
// in this situation, we need to add extra letterbox on top of our letterbox
|
// in this situation, we need to add extra letterbox on top of our letterbox
|
||||||
// this means we simply don't crop anything _at all_
|
// this means we simply don't crop anything _at all_
|
||||||
videoDimensions.xFactor = 1;
|
videoDimensions.xFactor = 1;
|
||||||
@ -177,12 +199,14 @@ class Scaler {
|
|||||||
// videoDimensions.yFactor = videoDimensions.xFactor;
|
// videoDimensions.yFactor = videoDimensions.xFactor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.log('info', 'scaler', "[Scaler::calculateCrop] Crop factor calculated — ", videoDimensions.xFactor);
|
|
||||||
|
|
||||||
// correct the factors
|
this.logger.log('info', 'scaler', "[Scaler::calculateCrop] Crop factor calculated — ", videoDimensions.xFactor);
|
||||||
videoDimensions.xFactor *= arCorrectionFactor;
|
|
||||||
videoDimensions.yFactor *= arCorrectionFactor;
|
// correct the scale factor
|
||||||
|
if (videoDimensions.arCorrectionFactor) {
|
||||||
|
videoDimensions.xFactor *= videoDimensions.arCorrectionFactor;
|
||||||
|
videoDimensions.yFactor *= videoDimensions.arCorrectionFactor;
|
||||||
|
}
|
||||||
|
|
||||||
return videoDimensions;
|
return videoDimensions;
|
||||||
}
|
}
|
@ -1,4 +1,9 @@
|
|||||||
import Stretch from '../../../common/enums/stretch.enum';
|
import StretchType from '../../../common/enums/StretchType.enum';
|
||||||
|
import BrowserDetect from '../../conf/BrowserDetect';
|
||||||
|
import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
|
||||||
|
import VideoData from '../video-data/VideoData';
|
||||||
|
import Logger from '../Logger';
|
||||||
|
import Settings from '../Settings';
|
||||||
|
|
||||||
// računa vrednosti za transform-scale (x, y)
|
// računa vrednosti za transform-scale (x, y)
|
||||||
// transform: scale(x,y) se uporablja za raztegovanje videa, ne pa za približevanje
|
// transform: scale(x,y) se uporablja za raztegovanje videa, ne pa za približevanje
|
||||||
@ -6,8 +11,20 @@ import Stretch from '../../../common/enums/stretch.enum';
|
|||||||
// transform: scale(x,y) is used for stretching, not zooming.
|
// transform: scale(x,y) is used for stretching, not zooming.
|
||||||
|
|
||||||
class Stretcher {
|
class Stretcher {
|
||||||
// internal variables
|
//#region flags
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region helper objects
|
||||||
|
conf: VideoData;
|
||||||
|
logger: Logger;
|
||||||
|
settings: Settings;
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region misc data
|
||||||
|
mode: any;
|
||||||
|
fixedStretchRatio: any;
|
||||||
|
//#endregion
|
||||||
|
|
||||||
// functions
|
// functions
|
||||||
constructor(videoData) {
|
constructor(videoData) {
|
||||||
@ -18,11 +35,11 @@ class Stretcher {
|
|||||||
this.fixedStretchRatio = undefined;
|
this.fixedStretchRatio = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
setStretchMode(stretchMode, fixedStretchRatio) {
|
setStretchMode(stretchMode, fixedStretchRatio?) {
|
||||||
if (stretchMode === Stretch.Default) {
|
if (stretchMode === StretchType.Default) {
|
||||||
this.mode = this.settings.getDefaultStretchMode(window.location.hostname);
|
this.mode = this.settings.getDefaultStretchMode(window.location.hostname);
|
||||||
} else {
|
} else {
|
||||||
if (stretchMode === Stretch.Fixed || stretchMode == Stretch.FixedSource) {
|
if (stretchMode === StretchType.Fixed || stretchMode == StretchType.FixedSource) {
|
||||||
this.fixedStretchRatio = fixedStretchRatio;
|
this.fixedStretchRatio = fixedStretchRatio;
|
||||||
}
|
}
|
||||||
this.mode = stretchMode;
|
this.mode = stretchMode;
|
||||||
@ -30,17 +47,17 @@ class Stretcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyConditionalStretch(stretchFactors, actualAr){
|
applyConditionalStretch(stretchFactors, actualAr){
|
||||||
var playerAr = this.conf.player.dimensions.width / this.conf.player.dimensions.height;
|
let playerAr = this.conf.player.dimensions.width / this.conf.player.dimensions.height;
|
||||||
var videoAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
|
let videoAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
|
||||||
|
|
||||||
if (! actualAr){
|
if (! actualAr){
|
||||||
actualAr = playerAr;
|
actualAr = playerAr;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newWidth = this.conf.video.offsetWidth * stretchFactors.xFactor;
|
let newWidth = this.conf.video.offsetWidth * stretchFactors.xFactor;
|
||||||
var newHeight = this.conf.video.offsetHeight * stretchFactors.yFactor;
|
let newHeight = this.conf.video.offsetHeight * stretchFactors.yFactor;
|
||||||
|
|
||||||
var actualWidth, actualHeight;
|
let actualWidth, actualHeight;
|
||||||
|
|
||||||
// determine the dimensions of the video (sans black bars) after scaling
|
// determine the dimensions of the video (sans black bars) after scaling
|
||||||
if(actualAr < videoAr){
|
if(actualAr < videoAr){
|
||||||
@ -51,11 +68,11 @@ class Stretcher {
|
|||||||
actualWidth = newWidth;
|
actualWidth = newWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
var minW = this.conf.player.dimensions.width * (1 - this.settings.active.stretch.conditionalDifferencePercent);
|
let minW = this.conf.player.dimensions.width * (1 - this.settings.active.stretch.conditionalDifferencePercent);
|
||||||
var maxW = this.conf.player.dimensions.width * (1 + this.settings.active.stretch.conditionalDifferencePercent);
|
let maxW = this.conf.player.dimensions.width * (1 + this.settings.active.stretch.conditionalDifferencePercent);
|
||||||
|
|
||||||
var minH = this.conf.player.dimensions.height * (1 - this.settings.active.stretch.conditionalDifferencePercent);
|
let minH = this.conf.player.dimensions.height * (1 - this.settings.active.stretch.conditionalDifferencePercent);
|
||||||
var maxH = this.conf.player.dimensions.height * (1 + this.settings.active.stretch.conditionalDifferencePercent);
|
let maxH = this.conf.player.dimensions.height * (1 + this.settings.active.stretch.conditionalDifferencePercent);
|
||||||
|
|
||||||
if (actualWidth >= minW && actualWidth <= maxW) {
|
if (actualWidth >= minW && actualWidth <= maxW) {
|
||||||
stretchFactors.xFactor *= this.conf.player.dimensions.width / actualWidth;
|
stretchFactors.xFactor *= this.conf.player.dimensions.width / actualWidth;
|
||||||
@ -143,7 +160,7 @@ squeezeFactor: ${squeezeFactor}`, '\nvideo', this.conf.video);
|
|||||||
return arCorrectionFactor;
|
return arCorrectionFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateStretch(actualAr, playerArOverride) {
|
calculateStretch(actualAr, playerArOverride?) {
|
||||||
const playerAr = playerArOverride || this.conf.player.dimensions.width / this.conf.player.dimensions.height;
|
const playerAr = playerArOverride || this.conf.player.dimensions.width / this.conf.player.dimensions.height;
|
||||||
const streamAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
|
const streamAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
|
||||||
|
|
||||||
@ -151,7 +168,7 @@ squeezeFactor: ${squeezeFactor}`, '\nvideo', this.conf.video);
|
|||||||
actualAr = playerAr;
|
actualAr = playerAr;
|
||||||
}
|
}
|
||||||
|
|
||||||
var stretchFactors = {
|
let stretchFactors: any = {
|
||||||
xFactor: 1,
|
xFactor: 1,
|
||||||
yFactor: 1
|
yFactor: 1
|
||||||
};
|
};
|
||||||
@ -187,6 +204,7 @@ squeezeFactor: ${squeezeFactor}`, '\nvideo', this.conf.video);
|
|||||||
|
|
||||||
this.logger.log('info', 'stretcher', "[Stretcher.js::calculateStretch] stretching strategy 3")
|
this.logger.log('info', 'stretcher', "[Stretcher.js::calculateStretch] stretching strategy 3")
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// player adds LETTERBOX
|
// player adds LETTERBOX
|
||||||
|
|
||||||
@ -229,6 +247,64 @@ squeezeFactor: ${squeezeFactor}`, '\nvideo', this.conf.video);
|
|||||||
|
|
||||||
return stretchFactors;
|
return stretchFactors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that <video> element is never both taller-ish and wider-ish than the screen, while in fullscreen
|
||||||
|
* on Chromium-based browsers.
|
||||||
|
*
|
||||||
|
* Workaround for Chrome/Edge issue where zooming too much results in video being stretched incorrectly.
|
||||||
|
*
|
||||||
|
* Bug description — if the following are true:
|
||||||
|
* * user is using Chrome or Edge (but surprisingly not Opera)
|
||||||
|
* * user is using hardware acceleration
|
||||||
|
* * user is using a noVideo card
|
||||||
|
* * user is in full screen mode
|
||||||
|
* * the video is both roughly taller and roughly wider than the monitor
|
||||||
|
* Then the video will do StretchType.Basic no matter what you put in `transform: scale(x,y)`.
|
||||||
|
*
|
||||||
|
* In practice, the issue appears slightly _before_ the last condition is met (video needs to be ~3434 px wide
|
||||||
|
* in order for this bug to trigger on my 3440x1440 display).
|
||||||
|
*
|
||||||
|
* Because this issue happens regardless of how you upscale the video (doesn't matter if you use transform:scale
|
||||||
|
* or width+height or anything else), the aspect ratio needs to be limited _before_ applying arCorrectionFactor
|
||||||
|
* (note that arCorrectionFactor is usually <= 1, as it conpensates for zooming that height=[>100%] on <video>
|
||||||
|
* style attribute does).
|
||||||
|
*/
|
||||||
|
chromeBugMitigation(stretchFactors) {
|
||||||
|
if (BrowserDetect.anyChromium && this.conf.player?.dimensions?.fullscreen) {
|
||||||
|
const playerAr = this.conf.player.dimensions.width / this.conf.player.dimensions.height;
|
||||||
|
const streamAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
|
||||||
|
|
||||||
|
let maxSafeAr;
|
||||||
|
|
||||||
|
if (playerAr >= (streamAr * 1.1)) {
|
||||||
|
maxSafeAr = (window.innerWidth * 0.997) / window.innerHeight;
|
||||||
|
} else if (playerAr < (streamAr * 0.95)) {
|
||||||
|
maxSafeAr = window.innerWidth / (window.innerHeight * 0.997);
|
||||||
|
} else {
|
||||||
|
// in some cases, we tolerate minor stretch to avoid tiny black bars
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxSafeStretchFactor = this.conf.resizer.scaler.calculateCropCore(
|
||||||
|
{
|
||||||
|
xFactor: 1,
|
||||||
|
yFactor: 1,
|
||||||
|
arCorrectionFactor: stretchFactors.arCorrectionFactor
|
||||||
|
},
|
||||||
|
maxSafeAr,
|
||||||
|
streamAr,
|
||||||
|
playerAr
|
||||||
|
).xFactor;
|
||||||
|
|
||||||
|
// console.info('Stretch factors before:', stretchFactors.xFactor, stretchFactors.yFactor, "max safe:", maxSafeStretchFactor, "max safe ar:", maxSafeAr);
|
||||||
|
|
||||||
|
stretchFactors.xFactor = Math.min(stretchFactors.xFactor, maxSafeStretchFactor);
|
||||||
|
stretchFactors.yFactor = Math.min(stretchFactors.yFactor, maxSafeStretchFactor);
|
||||||
|
|
||||||
|
return stretchFactors;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Stretcher;
|
export default Stretcher;
|
@ -1,19 +1,32 @@
|
|||||||
import Debug from '../../conf/Debug';
|
import Debug from '../../conf/Debug';
|
||||||
|
import Logger from '../Logger';
|
||||||
|
import VideoData from '../video-data/VideoData';
|
||||||
|
|
||||||
// računa približevanje ter računa/popravlja odmike videa
|
// računa približevanje ter računa/popravlja odmike videa
|
||||||
// calculates zooming and video offsets/panning
|
// calculates zooming and video offsets/panning
|
||||||
|
|
||||||
class Zoom {
|
class Zoom {
|
||||||
// functions
|
//#region flags
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region helper objects
|
||||||
|
conf: VideoData;
|
||||||
|
logger: Logger;
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region misc data
|
||||||
|
scale: number = 1;
|
||||||
|
logScale: number = 0;
|
||||||
|
scaleStep: number = 0.1;
|
||||||
|
minScale: number = -1; // 50% (log2(0.5) = -1)
|
||||||
|
maxScale: number = 3; // 800% (log2(8) = 3)
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
|
||||||
constructor(videoData) {
|
constructor(videoData) {
|
||||||
this.conf = videoData;
|
this.conf = videoData;
|
||||||
this.logger = videoData.logger;
|
this.logger = videoData.logger;
|
||||||
|
|
||||||
this.scale = 1;
|
|
||||||
this.logScale = 0;
|
|
||||||
this.scaleStep = 0.1;
|
|
||||||
this.minScale = -1; // 50% (log2(0.5) = -1)
|
|
||||||
this.maxScale = 3; // 800% (log2(8) = 3)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(){
|
reset(){
|
||||||
@ -41,7 +54,7 @@ class Zoom {
|
|||||||
this.conf.announceZoom(this.scale);
|
this.conf.announceZoom(this.scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
setZoom(scale, no_announce){
|
setZoom(scale: number, no_announce?){
|
||||||
this.logger.log('info', 'debug', "[Zoom::setZoom] Setting zoom to", scale, "!");
|
this.logger.log('info', 'debug', "[Zoom::setZoom] Setting zoom to", scale, "!");
|
||||||
|
|
||||||
// NOTE: SCALE IS NOT LOGARITHMIC
|
// NOTE: SCALE IS NOT LOGARITHMIC
|
330
src/ext/uw-bg.js
330
src/ext/uw-bg.js
@ -1,18 +1,9 @@
|
|||||||
import Debug from './conf/Debug.js';
|
/**
|
||||||
import BrowserDetect from './conf/BrowserDetect';
|
* NOTE: we cannot get rid of this js file. I tried for 30 seconds and I couldn't get
|
||||||
import CommsServer from './lib/comms/CommsServer';
|
* extension to work unless I kept this part of extension out of the ts file.
|
||||||
import Settings from './lib/Settings';
|
*/
|
||||||
import Logger from './lib/Logger';
|
|
||||||
|
|
||||||
import { sleep } from '../common/js/utils';
|
import UWServer from './UWServer';
|
||||||
|
|
||||||
// we need vue in bg script, so we can get vuex.
|
|
||||||
// and we need vuex so popup will be initialized
|
|
||||||
// after the first click without resorting to ugly,
|
|
||||||
// dirty hacks
|
|
||||||
import Vue from 'vue';
|
|
||||||
import Vuex from 'vuex';
|
|
||||||
import VuexWebExtensions from 'vuex-webextensions';
|
|
||||||
|
|
||||||
var BgVars = {
|
var BgVars = {
|
||||||
arIsActive: true,
|
arIsActive: true,
|
||||||
@ -20,316 +11,7 @@ var BgVars = {
|
|||||||
currentSite: ""
|
currentSite: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
class UWServer {
|
const server = new UWServer();
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.ports = [];
|
|
||||||
this.arIsActive = true;
|
|
||||||
this.hasVideos = false;
|
|
||||||
this.currentSite = "";
|
|
||||||
this.setup();
|
|
||||||
|
|
||||||
this.videoTabs = {};
|
|
||||||
this.currentTabId = 0;
|
|
||||||
this._gctimeout = undefined;
|
|
||||||
|
|
||||||
this.selectedSubitem = {
|
|
||||||
'siteSettings': undefined,
|
|
||||||
'videoSettings': undefined,
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uiLoggerInitialized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async setup() {
|
|
||||||
// logger is the first thing that goes up
|
|
||||||
const loggingOptions = {
|
|
||||||
isBackgroundScript: true,
|
|
||||||
allowLogging: true,
|
|
||||||
useConfFromStorage: true,
|
|
||||||
logAll: true,
|
|
||||||
fileOptions: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
consoleOptions: {
|
|
||||||
enabled: true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.logger = new Logger();
|
|
||||||
await this.logger.init(loggingOptions);
|
|
||||||
|
|
||||||
this.settings = new Settings({logger: this.logger});
|
|
||||||
await this.settings.init();
|
|
||||||
this.comms = new CommsServer(this);
|
|
||||||
this.comms.subscribe('show-logger', async () => await this.initUiAndShowLogger());
|
|
||||||
this.comms.subscribe('init-vue', async () => await this.initUi());
|
|
||||||
this.comms.subscribe('uwui-vue-initialized', () => this.uiLoggerInitialized = true);
|
|
||||||
this.comms.subscribe('emit-logs', () => {}); // we don't need to do anything, this gets forwarded to UI content script as is
|
|
||||||
|
|
||||||
|
|
||||||
if(BrowserDetect.firefox) {
|
|
||||||
browser.tabs.onActivated.addListener((m) => {this.onTabSwitched(m)});
|
|
||||||
} else if (BrowserDetect.anyChromium) {
|
|
||||||
chrome.tabs.onActivated.addListener((m) => {this.onTabSwitched(m)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async _promisifyTabsGet(browserObj, tabId){
|
|
||||||
return new Promise( (resolve, reject) => {
|
|
||||||
browserObj.tabs.get(tabId, (tab) => resolve(tab));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async injectCss(css, sender) {
|
|
||||||
try {
|
|
||||||
if (BrowserDetect.firefox || BrowserDetect.edge) {
|
|
||||||
browser.tabs.insertCSS(sender.tab.id, {code: css, cssOrigin: 'user', frameId: sender.frameId});
|
|
||||||
} else if (BrowserDetect.anyChromium) {
|
|
||||||
chrome.tabs.insertCSS(sender.tab.id, {code: css, cssOrigin: 'user', frameId: sender.frameId});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.logger.log('error','debug', '[UwServer::injectCss] Error while injecting css:', {error: e, css, sender});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async removeCss(css, sender) {
|
|
||||||
try {
|
|
||||||
if (BrowserDetect.firefox || BrowserDetect.edge) {
|
|
||||||
browser.tabs.removeCSS(sender.tab.id, {code: css, cssOrigin: 'user', frameId: sender.frameId});
|
|
||||||
} else if (BrowserDetect.anyChromium) {
|
|
||||||
// this doesn't work currently, but hopefully chrome will get this feature in the future
|
|
||||||
chrome.tabs.removeCSS(sender.tab.id, {code: css, cssOrigin: 'user', frameId: sender.frameId});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.logger.log('error','debug', '[UwServer::injectCss] Error while removing css:', {error: e, css, sender});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async replaceCss(oldCss, newCss, sender) {
|
|
||||||
if (oldCss !== newCss) {
|
|
||||||
this.injectCss(newCss, sender);
|
|
||||||
this.removeCss(oldCss, sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extractHostname(url){
|
|
||||||
var hostname;
|
|
||||||
|
|
||||||
if (!url) {
|
|
||||||
return "<no url>";
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract hostname
|
|
||||||
if (url.indexOf("://") > -1) { //find & remove protocol (http, ftp, etc.) and get hostname
|
|
||||||
hostname = url.split('/')[2];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
hostname = url.split('/')[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
hostname = hostname.split(':')[0]; //find & remove port number
|
|
||||||
hostname = hostname.split('?')[0]; //find & remove "?"
|
|
||||||
|
|
||||||
return hostname;
|
|
||||||
}
|
|
||||||
|
|
||||||
async onTabSwitched(activeInfo){
|
|
||||||
this.hasVideos = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.currentTabId = activeInfo.tabId; // just for readability
|
|
||||||
|
|
||||||
let tab;
|
|
||||||
if (BrowserDetect.firefox) {
|
|
||||||
tab = await browser.tabs.get(this.currentTabId);
|
|
||||||
} else if (BrowserDetect.anyChromium) {
|
|
||||||
tab = await this._promisifyTabsGet(chrome, this.currentTabId);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.currentSite = this.extractHostname(tab.url);
|
|
||||||
this.logger.log('info', 'debug', '[UwServer::onTabSwitched] user switched tab. New site:', this.currentSite);
|
|
||||||
} catch(e) {
|
|
||||||
this.logger.log('error', 'debug', '[UwServer::onTabSwitched] there was a problem getting currnet site:', e)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectedSubitem = {
|
|
||||||
'siteSettings': undefined,
|
|
||||||
'videoSettings': undefined,
|
|
||||||
}
|
|
||||||
//TODO: change extension icon based on whether there's any videos on current page
|
|
||||||
}
|
|
||||||
|
|
||||||
registerVideo(sender) {
|
|
||||||
this.logger.log('info', 'comms', '[UWServer::registerVideo] Registering video.\nsender:', sender);
|
|
||||||
|
|
||||||
const tabHostname = this.extractHostname(sender.tab.url);
|
|
||||||
const frameHostname = this.extractHostname(sender.url);
|
|
||||||
|
|
||||||
// preveri za osirotele/zastarele vrednosti ter jih po potrebi izbriši
|
|
||||||
// check for orphaned/outdated values and remove them if neccessary
|
|
||||||
if (this.videoTabs[sender.tab.id]?.host != tabHostname) {
|
|
||||||
delete this.videoTabs[sender.tab.id]
|
|
||||||
} else if(this.videoTabs[sender.tab.id]?.frames[sender.frameId]?.host != frameHostname) {
|
|
||||||
delete this.videoTabs[sender.tab.id].frames[sender.frameId];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.videoTabs[sender.tab.id]) {
|
|
||||||
this.videoTabs[sender.tab.id].frames[sender.frameId] = {
|
|
||||||
id: sender.frameId,
|
|
||||||
host: frameHostname,
|
|
||||||
url: sender.url,
|
|
||||||
registerTime: Date.now(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.videoTabs[sender.tab.id] = {
|
|
||||||
id: sender.tab.id,
|
|
||||||
host: tabHostname,
|
|
||||||
url: sender.tab.url,
|
|
||||||
frames: {}
|
|
||||||
};
|
|
||||||
this.videoTabs[sender.tab.id].frames[sender.frameId] = {
|
|
||||||
id: sender.frameId,
|
|
||||||
host: frameHostname,
|
|
||||||
url: sender.url,
|
|
||||||
registerTime: Date.now(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.log('info', 'comms', '[UWServer::registerVideo] Video registered. current videoTabs:', this.videoTabs);
|
|
||||||
}
|
|
||||||
|
|
||||||
unregisterVideo(sender) {
|
|
||||||
this.logger.log('info', 'comms', '[UwServer::unregisterVideo] Unregistering video.\nsender:', sender);
|
|
||||||
if (this.videoTabs[sender.tab.id]) {
|
|
||||||
if ( Object.keys(this.videoTabs[sender.tab.id].frames).length <= 1) {
|
|
||||||
delete this.videoTabs[sender.tab.id]
|
|
||||||
} else {
|
|
||||||
if(this.videoTabs[sender.tab.id].frames[sender.frameId]) {
|
|
||||||
delete this.videoTabs[sender.tab.id].frames[sender.frameId];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.logger.log('info', 'comms', '[UwServer::unregisterVideo] Video has been unregistered. Current videoTabs:', this.videoTabs);
|
|
||||||
}
|
|
||||||
|
|
||||||
setSelectedTab(menu, subitem) {
|
|
||||||
this.logger.log('info', 'comms', '[UwServer::setSelectedTab] saving selected tab for', menu, ':', subitem);
|
|
||||||
this.selectedSubitem[menu] = subitem;
|
|
||||||
}
|
|
||||||
|
|
||||||
async initUi() {
|
|
||||||
try {
|
|
||||||
if (BrowserDetect.firefox) {
|
|
||||||
await browser.tabs.executeScript({
|
|
||||||
file: '/ext/uw-ui.js',
|
|
||||||
allFrames: true,
|
|
||||||
});
|
|
||||||
} else if (BrowserDetect.anyChromium) {
|
|
||||||
await new Promise( resolve =>
|
|
||||||
chrome.tabs.executeScript({
|
|
||||||
file: '/ext/uw-ui.js',
|
|
||||||
allFrames: true,
|
|
||||||
}, () => resolve())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
this.logger.log('ERROR', 'uwbg', 'UI initialization failed. Reason:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async initUiAndShowLogger() {
|
|
||||||
// this implementation is less than optimal and very hacky, but it should work
|
|
||||||
// just fine for our use case.
|
|
||||||
this.uiLoggerInitialized = false;
|
|
||||||
|
|
||||||
await this.initUi();
|
|
||||||
|
|
||||||
await new Promise( async (resolve, reject) => {
|
|
||||||
// if content script doesn't give us a response within 5 seconds, something is
|
|
||||||
// obviously wrong and we stop waiting,
|
|
||||||
|
|
||||||
// oh and btw, resolve/reject do not break the loops, so we need to do that
|
|
||||||
// ourselves:
|
|
||||||
// https://stackoverflow.com/questions/55207256/will-resolve-in-promise-loop-break-loop-iteration
|
|
||||||
let isRejected = false;
|
|
||||||
setTimeout( async () => {isRejected = true; reject()}, 5000);
|
|
||||||
|
|
||||||
// check whether UI has been initiated on the FE. If it was, we resolve the
|
|
||||||
// promise and off we go
|
|
||||||
while (!isRejected) {
|
|
||||||
if (this.uiLoggerInitialized) {
|
|
||||||
resolve();
|
|
||||||
return; // remember the bit about resolve() not breaking the loop?
|
|
||||||
}
|
|
||||||
await sleep(100);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async getCurrentTab() {
|
|
||||||
if (BrowserDetect.firefox) {
|
|
||||||
return (await browser.tabs.query({active: true, currentWindow: true}))[0];
|
|
||||||
} else if (BrowserDetect.anyChromium) {
|
|
||||||
return new Promise((resolve, reject) => chrome.tabs.query({active: true, currentWindow: true}, (x) => resolve(x[0])));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getVideoTab() {
|
|
||||||
// friendly reminder: if current tab doesn't have a video,
|
|
||||||
// there won't be anything in this.videoTabs[this.currentTabId]
|
|
||||||
|
|
||||||
const ctab = await this.getCurrentTab();
|
|
||||||
|
|
||||||
if (!ctab || !ctab.id) {
|
|
||||||
return {
|
|
||||||
host: 'INVALID SITE',
|
|
||||||
frames: [],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.videoTabs[ctab.id]) {
|
|
||||||
// if video is older than PageInfo's video rescan period (+ 4000ms of grace),
|
|
||||||
// we clean it up from videoTabs[tabId].frames array.
|
|
||||||
const ageLimit = Date.now() - this.settings.active.pageInfo.timeouts.rescan - 4000;
|
|
||||||
console.log("videoTabs[tabId]:", this.videoTabs[ctab.id])
|
|
||||||
try {
|
|
||||||
for (const key in this.videoTabs[ctab.id].frames) {
|
|
||||||
if (this.videoTabs[ctab.id].frames[key].registerTime < ageLimit) {
|
|
||||||
delete this.videoTabs[ctab.id].frames[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// something went wrong. There's prolly no frames.
|
|
||||||
return {
|
|
||||||
host: this.extractHostname(ctab.url),
|
|
||||||
frames: [],
|
|
||||||
selected: this.selectedSubitem
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...this.videoTabs[ctab.id],
|
|
||||||
host: this.extractHostname(ctab.url),
|
|
||||||
selected: this.selectedSubitem
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// return something more or less empty if this tab doesn't have
|
|
||||||
// a video registered for it
|
|
||||||
return {
|
|
||||||
host: this.extractHostname(ctab.url),
|
|
||||||
frames: [],
|
|
||||||
selected: this.selectedSubitem
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// chrome shitiness mitigation
|
|
||||||
sendUnmarkPlayer(message) {
|
|
||||||
this.comms.sendUnmarkPlayer(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var server = new UWServer();
|
|
||||||
|
|
||||||
window.sendUnmarkPlayer = (message) => {
|
window.sendUnmarkPlayer = (message) => {
|
||||||
server.sendUnmarkPlayer(message)
|
server.sendUnmarkPlayer(message)
|
||||||
|
182
src/ext/uw.js
182
src/ext/uw.js
@ -1,14 +1,10 @@
|
|||||||
import Debug from './conf/Debug';
|
/**
|
||||||
import BrowserDetect from './conf/BrowserDetect';
|
* NOTE: we cannot get rid of this js file. I tried for 30 seconds and I couldn't get
|
||||||
import ExtensionMode from '../common/enums/extension-mode.enum';
|
* extension to work unless I kept this part of extension out of the ts file.
|
||||||
import Settings from './lib/Settings';
|
*/
|
||||||
import ActionHandler from './lib/ActionHandler';
|
|
||||||
import Comms from './lib/comms/Comms';
|
|
||||||
import CommsClient from './lib/comms/CommsClient';
|
|
||||||
import PageInfo from './lib/video-data/PageInfo';
|
|
||||||
import Logger from './lib/Logger';
|
|
||||||
import UWGlobals from './lib/UWGlobals';
|
|
||||||
|
|
||||||
|
import UWContent from './UWContent';
|
||||||
|
import BrowserDetect from './conf/BrowserDetect';
|
||||||
|
|
||||||
if(process.env.CHANNEL !== 'stable'){
|
if(process.env.CHANNEL !== 'stable'){
|
||||||
console.warn("\n\n\n\n\n\n ——— Sᴛλʀᴛɪɴɢ Uʟᴛʀᴀᴡɪᴅɪꜰʏ ———\n << ʟᴏᴀᴅɪɴɢ ᴍᴀɪɴ ꜰɪʟᴇ >>\n\n\n\n");
|
console.warn("\n\n\n\n\n\n ——— Sᴛλʀᴛɪɴɢ Uʟᴛʀᴀᴡɪᴅɪꜰʏ ———\n << ʟᴏᴀᴅɪɴɢ ᴍᴀɪɴ ꜰɪʟᴇ >>\n\n\n\n");
|
||||||
@ -28,169 +24,5 @@ if (BrowserDetect.edge) {
|
|||||||
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
|
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
|
||||||
}
|
}
|
||||||
|
|
||||||
class UW {
|
const main = new UWContent();
|
||||||
constructor(){
|
|
||||||
this.pageInfo = undefined;
|
|
||||||
this.comms = undefined;
|
|
||||||
this.settings = undefined;
|
|
||||||
this.actionHandler = undefined;
|
|
||||||
this.logger = undefined;
|
|
||||||
this.uiInitiated = false;
|
|
||||||
|
|
||||||
this.commsHandlers = {
|
|
||||||
'get-current-zoom': [() => this.pageInfo.requestCurrentZoom()],
|
|
||||||
'set-ar': [(message) => this.pageInfo.setAr({type: message.arg, ratio: message.customArg}, message.playing)],
|
|
||||||
'set-alignment': [(message) => {
|
|
||||||
this.pageInfo.setVideoAlignment(message.arg, message.playing);
|
|
||||||
this.pageInfo.restoreAr();
|
|
||||||
}],
|
|
||||||
'set-stretch': [(message) => this.pageInfo.setStretchMode(message.arg, message.playing, message.customArg)],
|
|
||||||
'set-keyboard': [(message) => this.pageInfo.setKeyboardShortcutsEnabled(message.arg)],
|
|
||||||
'autoar-start': [(message) => {
|
|
||||||
if (message.enabled !== false) {
|
|
||||||
this.pageInfo.initArDetection(message.playing);
|
|
||||||
this.pageInfo.startArDetection(message.playing);
|
|
||||||
} else {
|
|
||||||
this.pageInfo.stopArDetection(message.playing);
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
'pause-processing': [(message) => this.pageInfo.pauseProcessing(message.playing)],
|
|
||||||
'resume-processing': [(message) => this.pageInfo.resumeProcessing(message.autoArStatus, message.playing)],
|
|
||||||
'set-zoom': [(message) => this.pageInfo.setZoom(message.arg, true, message.playing)],
|
|
||||||
'change-zoom': [(message) => this.pageInfo.zoomStep(message.arg, message.playing)],
|
|
||||||
'mark-player': [(message) => this.pageInfo.markPlayer(message.name, message.color)],
|
|
||||||
'unmark-player': [() => this.pageInfo.unmarkPlayer()],
|
|
||||||
'autoar-set-manual-tick': [(message) => this.pageInfo.setManualTick(message.arg)],
|
|
||||||
'autoar-tick': [() => this.pageInfo.tick()],
|
|
||||||
'set-ar-persistence': [() => this.pageInfo.setArPersistence(message.arg)],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createUWGlobals() {
|
|
||||||
if (window.ultrawidify) {
|
|
||||||
window.ultrawidify.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
window.ultrawidify = new UWGlobals();
|
|
||||||
}
|
|
||||||
|
|
||||||
reloadSettings() {
|
|
||||||
this.logger.log('info', 'debug', 'Things happened in the popup. Will reload extension settings.');
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
async init(){
|
|
||||||
if (Debug.debug) {
|
|
||||||
console.log("[uw::main] loading configuration ...");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.createUWGlobals();
|
|
||||||
|
|
||||||
// logger init is the first thing that needs to run
|
|
||||||
try {
|
|
||||||
if (!this.logger) {
|
|
||||||
const loggingOptions = {
|
|
||||||
isContentScript: true,
|
|
||||||
allowLogging: true,
|
|
||||||
useConfFromStorage: true,
|
|
||||||
fileOptions: {
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
consoleOptions: {
|
|
||||||
"enabled": true,
|
|
||||||
"debug": true,
|
|
||||||
"init": true,
|
|
||||||
"settings": true,
|
|
||||||
"keyboard": true,
|
|
||||||
"mousemove": false,
|
|
||||||
"actionHandler": true,
|
|
||||||
"comms": true,
|
|
||||||
"playerDetect": true,
|
|
||||||
"resizer": true,
|
|
||||||
"scaler": true,
|
|
||||||
"stretcher": true,
|
|
||||||
// "videoRescan": true,
|
|
||||||
// "playerRescan": true,
|
|
||||||
"arDetect": true,
|
|
||||||
"arDetect_verbose": true
|
|
||||||
},
|
|
||||||
allowBlacklistedOrigins: {
|
|
||||||
'periodicPlayerCheck': false,
|
|
||||||
'periodicVideoStyleChangeCheck': false,
|
|
||||||
'handleMouseMove': false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.logger = new Logger();
|
|
||||||
await this.logger.init(loggingOptions);
|
|
||||||
|
|
||||||
// show popup if logging to file is enabled
|
|
||||||
if (this.logger.isLoggingAllowed() && this.logger.isLoggingToFile()) {
|
|
||||||
console.info("[uw::init] Logging is allowed! Initalizing vue and UI!");
|
|
||||||
|
|
||||||
// CommsClient is not initiated yet, so we use static comms to send the command
|
|
||||||
Comms.sendMessage({cmd: 'show-logger'});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error("logger init failed!", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// init() is re-run any time settings change
|
|
||||||
if (this.comms) {
|
|
||||||
this.comms.destroy();
|
|
||||||
}
|
|
||||||
if (!this.settings) {
|
|
||||||
this.settings = new Settings({
|
|
||||||
onSettingsChanged: () => this.reloadSettings(),
|
|
||||||
logger: this.logger
|
|
||||||
});
|
|
||||||
await this.settings.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.comms = new CommsClient('content-main-port', this.logger, this.commsHandlers);
|
|
||||||
|
|
||||||
// če smo razširitev onemogočili v nastavitvah, ne naredimo ničesar
|
|
||||||
// If extension is soft-disabled, don't do shit
|
|
||||||
|
|
||||||
var extensionMode = this.settings.getExtensionMode();
|
|
||||||
|
|
||||||
this.logger.log('info', 'debug', "[uw::init] Extension mode:" + (extensionMode < 0 ? "disabled" : extensionMode == '1' ? 'basic' : 'full'));
|
|
||||||
|
|
||||||
const isSiteDisabled = extensionMode === ExtensionMode.Disabled
|
|
||||||
|
|
||||||
if (isSiteDisabled) {
|
|
||||||
if (this.settings.getExtensionMode('@global') === ExtensionMode.Disabled) {
|
|
||||||
this.logger.log('info', 'debug', "[uw::init] EXTENSION DISABLED, THEREFORE WONT BE STARTED")
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (this.pageInfo) {
|
|
||||||
this.logger.log('info', 'debug', '[uw.js::setup] An instance of pageInfo already exists and will be destroyed.');
|
|
||||||
this.pageInfo.destroy();
|
|
||||||
}
|
|
||||||
this.pageInfo = new PageInfo(this.comms, this.settings, this.logger, extensionMode, isSiteDisabled);
|
|
||||||
this.logger.log('info', 'debug', "[uw.js::setup] pageInfo initialized.");
|
|
||||||
|
|
||||||
this.logger.log('info', 'debug', "[uw.js::setup] will try to initate ActionHandler.");
|
|
||||||
|
|
||||||
// start action handler only if extension is enabled for this site
|
|
||||||
if (!isSiteDisabled) {
|
|
||||||
if (this.actionHandler) {
|
|
||||||
this.actionHandler.destroy();
|
|
||||||
}
|
|
||||||
this.actionHandler = new ActionHandler(this.pageInfo);
|
|
||||||
this.actionHandler.init();
|
|
||||||
|
|
||||||
this.logger.log('info', 'debug', "[uw.js::setup] ActionHandler initiated.");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
this.logger.log('error', 'debug', "[uw::init] FAILED TO START EXTENSION. Error:", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var main = new UW();
|
|
||||||
main.init();
|
main.init();
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "Ultrawidify",
|
"name": "Ultrawidify",
|
||||||
"description": "Removes black bars on ultrawide videos and offers advanced options to fix aspect ratio.",
|
"description": "Removes black bars on ultrawide videos and offers advanced options to fix aspect ratio.",
|
||||||
"version": "4.5.2",
|
"version": "5.0.0",
|
||||||
"applications": {
|
"applications": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
"id": "{cf02b1a7-a01a-4e37-a609-516a283f1ed3}"
|
"id": "{cf02b1a7-a01a-4e37-a609-516a283f1ed3}"
|
||||||
|
@ -111,11 +111,11 @@
|
|||||||
<script>
|
<script>
|
||||||
import Donate from '../common/misc/Donate.vue';
|
import Donate from '../common/misc/Donate.vue';
|
||||||
import SuperAdvancedSettings from './SuperAdvancedSettings.vue';
|
import SuperAdvancedSettings from './SuperAdvancedSettings.vue';
|
||||||
import Debug from '../ext/conf/Debug.js';
|
import Debug from '../ext/conf/Debug';
|
||||||
import BrowserDetect from '../ext/conf/BrowserDetect.js';
|
import BrowserDetect from '../ext/conf/BrowserDetect';
|
||||||
import ExtensionConf from '../ext/conf/ExtensionConf.js';
|
import ExtensionConf from '../ext/conf/ExtensionConf';
|
||||||
import ObjectCopy from '../ext/lib/ObjectCopy.js';
|
import ObjectCopy from '../ext/lib/ObjectCopy';
|
||||||
import Settings from '../ext/lib/Settings.js';
|
import Settings from '../ext/lib/Settings';
|
||||||
import GeneralSettings from './GeneralSettings';
|
import GeneralSettings from './GeneralSettings';
|
||||||
import ControlsSettings from './controls-settings/ControlsSettings';
|
import ControlsSettings from './controls-settings/ControlsSettings';
|
||||||
import AddEditActionPopup from './controls-settings/AddEditActionPopup';
|
import AddEditActionPopup from './controls-settings/AddEditActionPopup';
|
||||||
|
@ -185,7 +185,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-input">
|
<div class="flex flex-input">
|
||||||
<input type="text"
|
<input type="text"
|
||||||
v-model="settings.active.arDetect.allowedMisaligned"
|
v-model="settings.active.arDetect.pillarTest.allowMisaligned"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,16 +57,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row button-box">
|
<div class="flex flex-row button-box">
|
||||||
<Button label="Left"
|
<Button label="Left"
|
||||||
:selected="settings.active.sites['@global'].videoAlignment === VideoAlignment.Left"
|
:selected="settings.active.sites['@global'].videoAlignment === VideoAlignmentType.Left"
|
||||||
@click.native="setDefaultvideoAlignment(VideoAlignment.Left)">
|
@click.native="setDefaultvideoAlignment(VideoAlignmentType.Left)">
|
||||||
</Button>
|
</Button>
|
||||||
<Button label="Center"
|
<Button label="Center"
|
||||||
:selected="settings.active.sites['@global'].videoAlignment === VideoAlignment.Center"
|
:selected="settings.active.sites['@global'].videoAlignment === VideoAlignmentType.Center"
|
||||||
@click.native="setDefaultvideoAlignment(VideoAlignment.Center)">
|
@click.native="setDefaultvideoAlignment(VideoAlignmentType.Center)">
|
||||||
</Button>
|
</Button>
|
||||||
<Button label="Right"
|
<Button label="Right"
|
||||||
:selected="settings.active.sites['@global'].videoAlignment === VideoAlignment.Right"
|
:selected="settings.active.sites['@global'].videoAlignment === VideoAlignmentType.Right"
|
||||||
@click.native="setDefaultvideoAlignment(VideoAlignment.Right)">
|
@click.native="setDefaultvideoAlignment(VideoAlignmentType.Right)">
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -75,20 +75,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row button-box">
|
<div class="flex flex-row button-box">
|
||||||
<Button label="Don't stretch"
|
<Button label="Don't stretch"
|
||||||
:selected="settings.active.sites['@global'].stretch === Stretch.NoStretch"
|
:selected="settings.active.sites['@global'].stretch === StretchType.NoStretch"
|
||||||
@click.native="setDefaultStretchingMode(Stretch.NoStretch)">
|
@click.native="setDefaultStretchingMode(StretchType.NoStretch)">
|
||||||
</Button>
|
</Button>
|
||||||
<Button label="Basic stretch"
|
<Button label="Basic stretch"
|
||||||
:selected="settings.active.sites['@global'].stretch === Stretch.Basic"
|
:selected="settings.active.sites['@global'].stretch === StretchType.Basic"
|
||||||
@click.native="setDefaultStretchingMode(Stretch.Basic)">
|
@click.native="setDefaultStretchingMode(StretchType.Basic)">
|
||||||
</Button>
|
</Button>
|
||||||
<Button label="Hybrid stretch"
|
<Button label="Hybrid stretch"
|
||||||
:selected="settings.active.sites['@global'].stretch === Stretch.Hybrid"
|
:selected="settings.active.sites['@global'].stretch === StretchType.Hybrid"
|
||||||
@click.native="setDefaultStretchingMode(Stretch.Hybrid)">
|
@click.native="setDefaultStretchingMode(StretchType.Hybrid)">
|
||||||
</Button>
|
</Button>
|
||||||
<Button label="Thin borders only"
|
<Button label="Thin borders only"
|
||||||
:selected="settings.active.sites['@global'].stretch === Stretch.Conditional"
|
:selected="settings.active.sites['@global'].stretch === StretchType.Conditional"
|
||||||
@click.native="setDefaultStretchingMode(Stretch.Conditional)"
|
@click.native="setDefaultStretchingMode(StretchType.Conditional)"
|
||||||
>
|
>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@ -108,7 +108,7 @@
|
|||||||
<div class="flex flex-input">
|
<div class="flex flex-input">
|
||||||
<input type="number"
|
<input type="number"
|
||||||
step="any"
|
step="any"
|
||||||
:value="settings.active.stretch.conditionalDifferencePercent"
|
:value="settings.active.StretchType.conditionalDifferencePercent"
|
||||||
@input="updateStretchThreshold($event.target.value)"
|
@input="updateStretchThreshold($event.target.value)"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@ -152,9 +152,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Button from '../common/components/Button';
|
import Button from '../common/components/Button';
|
||||||
import Stretch from '../common/enums/stretch.enum';
|
import StretchType from '../common/enums/StretchType.enum';
|
||||||
import ExtensionMode from '../common/enums/extension-mode.enum';
|
import ExtensionMode from '../common/enums/ExtensionMode.enum';
|
||||||
import VideoAlignment from '../common/enums/video-alignment.enum';
|
import VideoAlignmentType from '../common/enums/VideoAlignmentType.enum';
|
||||||
import BrowserDetect from '../ext/conf/BrowserDetect';
|
import BrowserDetect from '../ext/conf/BrowserDetect';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -198,7 +198,7 @@ export default {
|
|||||||
if (!newThreshold || isNaN(newThreshold)) {
|
if (!newThreshold || isNaN(newThreshold)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.settings.active.stretch.conditionalDifferencePercent = newThreshold;
|
this.settings.active.StretchType.conditionalDifferencePercent = newThreshold;
|
||||||
this.settings.save();
|
this.settings.save();
|
||||||
},
|
},
|
||||||
resetSettings() {
|
resetSettings() {
|
||||||
|
@ -102,7 +102,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ShortcutButton from '../../common/components/ShortcutButton.vue'
|
import ShortcutButton from '../../common/components/ShortcutButton.vue'
|
||||||
import Stretch from '../../common/enums/stretch.enum';
|
import StretchType from '../../common/enums/StretchType.enum';
|
||||||
import KeyboardShortcutParser from '../../common/js/KeyboardShortcutParser';
|
import KeyboardShortcutParser from '../../common/js/KeyboardShortcutParser';
|
||||||
import CommandChain from './command-builder/CommandChain';
|
import CommandChain from './command-builder/CommandChain';
|
||||||
import CommandAddEdit from './command-builder/CommandAddEdit';
|
import CommandAddEdit from './command-builder/CommandAddEdit';
|
||||||
|
@ -111,7 +111,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Button from '../../common/components/Button';
|
import Button from '../../common/components/Button';
|
||||||
import Stretch from '../../common/enums/stretch.enum';
|
import StretchType from '../../common/enums/StretchType.enum';
|
||||||
import ActionAlt from '../../common/components/ActionAlt';
|
import ActionAlt from '../../common/components/ActionAlt';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -89,7 +89,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ActionList from '../../../ext/conf/ActionList';
|
import ActionList from '../../../ext/conf/ActionList';
|
||||||
import Stretch from '../../../common/enums/stretch.enum';
|
import StretchType from '../../../common/enums/StretchType.enum';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
|
@ -219,7 +219,7 @@ import Settings from '../ext/lib/Settings';
|
|||||||
import ExecAction from './js/ExecAction.js';
|
import ExecAction from './js/ExecAction.js';
|
||||||
import DefaultSettingsPanel from './panels/DefaultSettingsPanel';
|
import DefaultSettingsPanel from './panels/DefaultSettingsPanel';
|
||||||
import AboutPanel from './panels/AboutPanel';
|
import AboutPanel from './panels/AboutPanel';
|
||||||
import ExtensionMode from '../common/enums/extension-mode.enum';
|
import ExtensionMode from '../common/enums/ExtensionMode.enum';
|
||||||
import Logger from '../ext/lib/Logger';
|
import Logger from '../ext/lib/Logger';
|
||||||
import {ChromeShittinessMitigations as CSM} from '../common/js/ChromeShittinessMitigations';
|
import {ChromeShittinessMitigations as CSM} from '../common/js/ChromeShittinessMitigations';
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ class ExecAction {
|
|||||||
this.settings.active.sites[site].stretch = cmd.arg;
|
this.settings.active.sites[site].stretch = cmd.arg;
|
||||||
} else if (cmd.action === "set-alignment") {
|
} else if (cmd.action === "set-alignment") {
|
||||||
this.settings.active.sites[site].videoAlignment = cmd.arg;
|
this.settings.active.sites[site].videoAlignment = cmd.arg;
|
||||||
} else if (cmd.action === "set-extension-mode") {
|
} else if (cmd.action === "set-ExtensionMode") {
|
||||||
this.settings.active.sites[site].mode = cmd.arg;
|
this.settings.active.sites[site].mode = cmd.arg;
|
||||||
} else if (cmd.action === "set-autoar-mode") {
|
} else if (cmd.action === "set-autoar-mode") {
|
||||||
this.settings.active.sites[site].autoar = cmd.arg;
|
this.settings.active.sites[site].autoar = cmd.arg;
|
||||||
|
@ -130,9 +130,9 @@
|
|||||||
import ShortcutButton from '../../common/components/ShortcutButton.vue';
|
import ShortcutButton from '../../common/components/ShortcutButton.vue';
|
||||||
import QsElement from '../../common/components/QsElement.vue';
|
import QsElement from '../../common/components/QsElement.vue';
|
||||||
import QuerySelectorSetting from '../../common/components/QuerySelectorSetting.vue';
|
import QuerySelectorSetting from '../../common/components/QuerySelectorSetting.vue';
|
||||||
import ExtensionMode from '../../common/enums/extension-mode.enum';
|
import ExtensionMode from '../../common/enums/ExtensionMode.enum';
|
||||||
import VideoAlignment from '../../common/enums/video-alignment.enum';
|
import VideoAlignmentType from '../../common/enums/VideoAlignmentType.enum';
|
||||||
import Stretch from '../../common/enums/stretch.enum';
|
import StretchType from '../../common/enums/StretchType.enum';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
QuerySelectorSetting,
|
QuerySelectorSetting,
|
||||||
@ -186,8 +186,8 @@ export default {
|
|||||||
mode: ExtensionMode.Default,
|
mode: ExtensionMode.Default,
|
||||||
autoar: ExtensionMode.Default,
|
autoar: ExtensionMode.Default,
|
||||||
type: 'user-added',
|
type: 'user-added',
|
||||||
stretch: Stretch.Default,
|
stretch: StretchType.Default,
|
||||||
videoAlignment: VideoAlignment.Default,
|
videoAlignment: VideoAlignmentType.Default,
|
||||||
keyboardShortcutsEnabled: ExtensionMode.Default,
|
keyboardShortcutsEnabled: ExtensionMode.Default,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ import ExecAction from '../js/ExecAction';
|
|||||||
import KeyboardShortcutParser from '../../common/js/KeyboardShortcutParser';
|
import KeyboardShortcutParser from '../../common/js/KeyboardShortcutParser';
|
||||||
import ShortcutButton from '../../common/components/ShortcutButton';
|
import ShortcutButton from '../../common/components/ShortcutButton';
|
||||||
import ComputeActionsMixin from '../../common/mixins/ComputeActionsMixin';
|
import ComputeActionsMixin from '../../common/mixins/ComputeActionsMixin';
|
||||||
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
|
import CropModePersistence from '../../common/enums/CropModePersistence.enum';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
@ -2,13 +2,16 @@
|
|||||||
<div>
|
<div>
|
||||||
<h2>What's new</h2>
|
<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>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">4.5.2</p>
|
<p class="label">4.5.3</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
Fixed the issue where videos would sometimes get misaligned while using hybrid stretch, except for real this time. (<a href="https://github.com/tamius-han/ultrawidify/issues/125">#125</a>)
|
Provided a workaround for the fullscreen stretching bug Chrome 88 (or a recent Windows 10 update) introduced for nVidia users using hardware acceleration on Windows 10. In order to mitigate this bug, Ultrawidify needs to keep a 5-10 px wide black border while watching videos in full screen. This bug is also present in Edge.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Improved DRM detection (the 'autodetection cannot work on this site' popup should now no longer show up on the sites where autodetection <i>can</i> work)
|
<b>[4.5.3.1]</b> Fixed binding for letterbox misalignment treshold binding in settings.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>[4.5.3.2]</b> Removed false positive "this extension can't work due to DRM" notifications.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./ts-out",
|
||||||
|
"allowJs": true,
|
||||||
|
"target": "es2018",
|
||||||
|
"types": [
|
||||||
|
"chrome",
|
||||||
|
"node"
|
||||||
|
],
|
||||||
|
"typeRoots": [
|
||||||
|
"node_modules/@types",
|
||||||
|
"node_modules/web-ext-types"
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"include": [ "./src/**/*" ],
|
||||||
|
}
|
@ -22,19 +22,23 @@ const config = {
|
|||||||
path: __dirname + `/dist-${process.env.BROWSER == 'firefox' ? 'ff' : process.env.BROWSER}`,
|
path: __dirname + `/dist-${process.env.BROWSER == 'firefox' ? 'ff' : process.env.BROWSER}`,
|
||||||
filename: '[name].js',
|
filename: '[name].js',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
devtool: "source-map",
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
// maybe we'll move to TS some day, but today is not the day
|
// maybe we'll move to TS some day, but today is not the day
|
||||||
extensions: [
|
extensions: [
|
||||||
// '.ts', '.tsx',
|
'.ts', '.tsx',
|
||||||
'.js', '.vue'
|
'.js', '.vue'
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
// {
|
{
|
||||||
// test: /\.tsx?$/,
|
test: /\.ts$/,
|
||||||
// loader: 'ts-loader',
|
loader: 'ts-loader',
|
||||||
// },
|
exclude: /node_modules/
|
||||||
|
},
|
||||||
{
|
{
|
||||||
test: /\.vue$/,
|
test: /\.vue$/,
|
||||||
loaders: 'vue-loader',
|
loaders: 'vue-loader',
|
||||||
@ -121,7 +125,7 @@ const config = {
|
|||||||
.substr(2) // YYYY -> YY
|
.substr(2) // YYYY -> YY
|
||||||
.replace('-', '') // YY-MM-DD -> YYMM-DD
|
.replace('-', '') // YY-MM-DD -> YYMM-DD
|
||||||
.replace('-', '.') // YYMM-DD -> YYMM.DD
|
.replace('-', '.') // YYMM-DD -> YYMM.DD
|
||||||
}.${process.env.BUILD_NUMBER ?? 0}`;
|
}.${process.env.BUILD_NUMBER === undefined ? 0 : process.env.BUILD_NUMBER}`;
|
||||||
jsonContent.browser_action.default_title = "Ultrawidify Nightly";
|
jsonContent.browser_action.default_title = "Ultrawidify Nightly";
|
||||||
|
|
||||||
// because we don't want web-ext to submit this as proper release
|
// because we don't want web-ext to submit this as proper release
|
||||||
@ -137,7 +141,7 @@ const config = {
|
|||||||
.substr(2) // YYYY -> YY
|
.substr(2) // YYYY -> YY
|
||||||
.replace('-', '') // YY-MM-DD -> YYMM-DD
|
.replace('-', '') // YY-MM-DD -> YYMM-DD
|
||||||
.replace('-', '.') // YYMM-DD -> YYMM.DD
|
.replace('-', '.') // YYMM-DD -> YYMM.DD
|
||||||
}.${process.env.BUILD_NUMBER ?? 0}`;
|
}.${process.env.BUILD_NUMBER === undefined ? 0 : process.env.BUILD_NUMBER}`;
|
||||||
jsonContent.browser_action.default_title = "Ultrawidify Testing";
|
jsonContent.browser_action.default_title = "Ultrawidify Testing";
|
||||||
|
|
||||||
// because we don't want web-ext to submit this as proper release
|
// because we don't want web-ext to submit this as proper release
|
||||||
|
Loading…
Reference in New Issue
Block a user