Merge branch 'typescriptify'

This commit is contained in:
Tamius Han 2021-03-06 04:02:59 +01:00
commit 6a1f07d881
71 changed files with 4898 additions and 2188 deletions

View File

@ -6,9 +6,9 @@
["@babel/preset-env", {
"useBuiltIns": false,
"targets": {
"esmodules": true,
},
}],
"esmodules": true
}
}]
]
}
// {

View File

@ -8,12 +8,16 @@
* Settings page looks ugly af right now. Maybe fix it some time later
* other bug fixes
## v5.0 (planned major)
## v6.0 (planned major)
* WebGL autodetection
* in-player GUI
* Fix UI logger
## v5.x (next major)
* Migrate main scripts to typescript (vue is currently not included)
## v4.x (current major)
### v4.5.3

View File

@ -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

3230
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "ultrawidify",
"version": "4.5.3",
"version": "5.0.0",
"description": "Aspect ratio fixer for youtube and other sites, with automatic aspect ratio detection. Supports ultrawide and other ratios.",
"author": "Tamius Han <tamius.han@gmail.com>",
"scripts": {
@ -21,8 +21,11 @@
},
"dependencies": {
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@types/chrome": "0.0.129",
"@types/core-js": "^2.5.3",
"@types/es6-promise": "^3.3.0",
"@types/firefox": "0.0.30",
"@types/node": "^14.14.25",
"@vue/cli": "^4.5.9",
"bootstrap": "^4.5.3",
"bootstrap-icons": "^1.1.0",
@ -31,17 +34,21 @@
"concurrently": "^5.2.0",
"fs-extra": "^7.0.1",
"json-cyclic": "0.0.3",
"lodash": "^4.17.20",
"vue": "^3.0.0-beta.1",
"vuex": "^4.0.0-alpha.1",
"vuex-webextensions": "^1.3.0"
"vuex-webextensions": "^1.3.0",
"webextension-polyfill-ts": "^0.24.0"
},
"devDependencies": {
"@babel/core": "^7.10.5",
"@babel/core": "^7.12.13",
"@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",
"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",
"cross-env": "^5.2.0",
"css-loader": "^0.28.11",
@ -50,9 +57,10 @@
"mini-css-extract-plugin": "^0.4.4",
"node-sass": "^4.14.1",
"sass-loader": "^7.1.0",
"ts-loader": "^8.0.16",
"vue-cli-plugin-vue-next": "~0.1.4",
"vue-loader": "^16.0.0",
"web-ext-types": "^2.1.0",
"web-ext-types": "^2.3.0",
"webextension-polyfill": "^0.6.0",
"webpack": "^4.44.0",
"webpack-chrome-extension-reloader": "^0.8.3",

View File

@ -5,7 +5,7 @@
</div>
<div class="flex action-name">
<span v-if="action.cmd && action.cmd.length > 1 || action.cmd[0].action === 'set-ar' && action.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> &nbsp; &nbsp;
<span class="icon" @click="editAction()">🖉</span> &nbsp; &nbsp;
{{action.name}}
@ -108,8 +108,8 @@
</template>
<script>
import Stretch from '../enums/stretch.enum';
import AspectRatio from '../enums/aspect-ratio.enum';
import StretchType from '../enums/StretchType.enum';
import AspectRatioType from '../enums/AspectRatioType.enum';
import KeyboardShortcutParser from '../js/KeyboardShortcutParser';

View File

@ -46,7 +46,7 @@
</template>
<script>
import Stretch from '../enums/stretch.enum';
import StretchType from '../enums/StretchType.enum';
import KeyboardShortcutParser from '../js/KeyboardShortcutParser'
export default {

View File

@ -0,0 +1,7 @@
enum AntiGradientMode {
Disabled = 0,
Lax = 1,
Strict = 2
}
export default AntiGradientMode;

View 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;

View File

@ -0,0 +1,9 @@
enum CropModePersistence {
Default = -1,
Disabled = 0,
UntilPageReload = 1,
CurrentSession = 2,
Forever = 3,
}
export default CropModePersistence;

View File

@ -0,0 +1,10 @@
enum ExtensionMode {
AutoDisabled = -2,
Disabled = -1,
Default = 0,
Whitelist = 1,
Basic = 2,
Enabled = 3,
};
export default ExtensionMode;

View File

@ -0,0 +1,11 @@
enum StretchType {
NoStretch = 0,
Basic = 1,
Hybrid = 2,
Conditional = 3,
Fixed = 4,
FixedSource = 5,
Default = -1
};
export default StretchType;

View File

@ -0,0 +1,8 @@
enum VideoAlignmentType {
Left = 0,
Center = 1,
Right = 2,
Default = -1
};
export default VideoAlignmentType;

View File

@ -1,7 +0,0 @@
var AntiGradientMode = Object.freeze({
Disabled: 0,
Lax: 1,
Strict: 2
});
export default AntiGradientMode;

View File

@ -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;

View File

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

View File

@ -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;

View File

@ -1,11 +0,0 @@
var Stretch = Object.freeze({
NoStretch: 0,
Basic: 1,
Hybrid: 2,
Conditional: 3,
Fixed: 4,
FixedSource: 5,
Default: -1
});
export default Stretch;

View File

@ -1,8 +0,0 @@
var VideoAlignment = Object.freeze({
Left: 0,
Center: 1,
Right: 2,
Default: -1
});
export default VideoAlignment;

View 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;

View File

@ -10,7 +10,7 @@ export default {
}) || [];
},
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(){
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-autoar-mode') || [];

View File

@ -1,299 +1,176 @@
<template>
<div v-if="uiVisible" class="uw-hover uv-hover-trigger-region">
<div class="flex flex-column flex-center">
<div class="text panel">
<h1>Your browser is incompatible with this extension on this site</h1>
<!-- <p><sup>2020-12-22</sup></p> -->
<p>
In October 2020, Microsoft Edge received an update that breaks video playback on sites that utilize DRM in certain cases.
As a result of this bug, cropped videos will be displayed incorrectly. This issue cannot be fixed by any extension, and
<b>using this extension (and its alternatives) on this site may make things worse</b> even if you only set aspect ratio manually.
</p>
<p>
I have attempted all possible workarounds and none of them work.
</p>
<p>
I would like to ask a couple of favours:
</p>
<p>
<ul>
<li>
<b>Report this issue to Microsoft.</b>
I have already done so, however such issues get noticed quicker if more people report them.
<div class="vspc"></div>
I have prepared a description of the problem you can find it below (you may need to scroll).
</li>
<li>
<b>Please notify me when Microsoft fixes this issue.</b>
I use windows only sporadically and cannot check whether Edge developers have fixed the issue or not.
You can contact me by opening an issue on <a href="https://github.com/tamius-han/ultrawidify/issues" target="_blank">github</a> or
sending me <a href="mailto:tamius.han@gmail.com" target="_blank">an email</a>. You can also <a href="https://www.reddit.com/message/compose?to=xternal7" target="_blank">PM me on reddit</a>.
<div class="vspc"></div>
Please include the following text with your message:
<div class="vspc"></div>
<i>{{userAgent}}</i>
<div class="vspc"></div>
You can determine whether the issue is fixed by attempting to watch 21:9 movie on netflix and compare what you see to the screenshots I included at the bottom.
</li>
<li>
<small>
<b>Avoid leaving one-star reviews on Chrome Web Store.</b> I don't usually whine about one star
reviews, but Chrome Web Store is for Google Chrome users. As such, I cannot optimize the extension
for Edge in my Chrome Web Store submission. Furthermore, people reading reviews in Chrome
Web Store are interested in knowing how extension behaves in Google Chrome. The experience you
get in Microsoft Edge is irrelevant to them.
<div class="vspc"></div>
If you wish the best experience, you should install this addon from Edge Addons store (once I make
it available on that store again).
<div class="vspc"></div>
Thanks for being understanding.
</small>
</li>
</ul>
</p>
<p>
<b>Thanks for your help in advance. It's much appreciated.</b>
</p>
<p>&nbsp;</p>
<p>
If you're interested in more details about why this happens, you can find more details in <a href="https://stuff.tamius.net/sacred-texts/2020/12/22/ultrawidify-and-edge-2020-edition/" target="_blank">this blogpost</a>.
</p>
<p>
Finished reading? <b><a @click="uiVisible=false">Hide this popup</a></b>.
</p>
<p>
In order to disable this popup forever, open the ultrawidify popup, click on 'site settings' and disable automatic aspect ratio detection for this site.
You should probably even disable the extension for this site altogether for the time being.
</p>
<p>
<br/>
<br/>
</p>
<h3>Help by reporting this issue to Microsoft</h3>
<p><b>Go to the settings menu</b> <small>(upper right corner of the window, three dots)</small> <b> Help and feedback</b> <small>(second option from the bottom)</small> <b> Send feedback.</b> (Alternatively, press Alt + Shift + I)</p>
<p>Enter this in the first box:</p>
<p>
<br/>
<i>
Videos on sites that utilize DRM protection schemes are not being scaled correctly. If a part of a DRM-protected video is being displayed outside the boundaries of the browser window,
Edge will scale the video to only fit the portion of the video tag that is currently being displayed on the screen, rather than filling the entire video tag. This causes videos appear
differently than website developers intended at best, and breaking certain websites at worst.
</i>
<br/>
</p>
<p>Or something along these lines. Click 'send' when you're done.</p>
<p>It's the squeaky wheel that gets the grease: developers tend to prioritize issues that affect more people. The more people report this issue, the more likely it is for developers to notice it.</p>
</div>
<div class="image-examples panel">
<h3>How can one tell when the Edge bug is fixed?</h3>
<p>
When 21:9 movies on netflix look like this:
</p>
<p>
<img :src="getUrl('res/img/git-ignore/edge-demo-working.jpg')" />
</p>
<p>
And not like this:
</p>
<p>
<img :src="getUrl('res/img/git-ignore/edge-demo.jpg')" />
</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<div class="uw-hover uv-hover-trigger-region">
TEST CONTENT
</div>
<div class="popup-panel">
<div class="tab-row flex flex-row">
<div class="tab">
todo: icon<br/>
Video options
</div>
</div>
<div>sudpo
<!-- Panel section -->
<template v-if="settingsInitialized">
<VideoSettings
:settings="settings"
></VideoSettings>
<ResizerDebugPanel :debugData="debugData">
</ResizerDebugPanel>
</template>
</div>
</div>
</template>
<script>
import VideoSettings from './PlayerUiPanels/VideoSettings.vue'
import { mapState } from 'vuex';
import Icon from '../common/components/Icon';
import ResizerDebugPanel from './PlayerUiPanels/ResizerDebugPanelComponent';
import BrowserDetect from '../ext/conf/BrowserDetect';
import ExecAction from './ui-libs/ExecAction';
import Logger from '../ext/lib/Logger';
import Settings from '../ext/lib/Settings';
import FontLoader from '../ext/lib/uwui/FontLoader';
export default {
components: {
Icon,
ResizerDebugPanel, VideoSettings
},
data() {
return {
// component properties
settings: {},
settingsInitialized: false,
execAction: new ExecAction(),
logger: null,
uiVisible: true,
userAgent: window.navigator.userAgent
debugData: {
resizer: {},
player: {},
},
debugDataPrettified: ''
};
},
created() {
FontLoader.loadFonts();
},
computed: {
...mapState([
'showUi',
'resizerDebugData',
'playerDebugData'
]),
windowWidth: () => {
return window.innerWidth;
},
windowHeight: () => {
return window.innerHeight;
},
},
watch: {
showUi(visible) {
if (visible !== undefined) {
this.uiVisible = visible;
}
},
resizerDebugData(newData) {
this.debugData.resizer = newData;
this.debugDataPrettified = JSON.stringify(this.debugData, null, 2);
},
playerDebugData(newData) {
this.debugData.player = newData;
this.debugDataPrettified = JSON.stringify(this.debugData, null, 2);
}
},
async created() {
try {
this.logger = new Logger();
await this.logger.init({
allowLogging: true,
});
this.settings = new Settings({afterSettingsSaved: this.updateConfig, logger: this.logger});
await this.settings.init();
this.settingsInitialized = true;
this.execAction.setSettings(this.settings);
} catch (e) {
console.error('Failed to initiate ultrawidify player ui.', e);
}
},
methods: {
getUrl(url) {
return BrowserDetect.getURL(url);
},
async hidePopupForever() {
const settings = new Settings();
await settings.init();
if (!settings.active.mutedNotifications) {
settings.active.mutedNotifications = {};
}
if (!settings.active.mutedNotifications?.browserSpecific) {
settings.active.mutedNotifications.browserSpecific = {
edge: {
brokenDrm: {
}
}
};
}
settings.active.mutedNotifications.browserSpecific.edge.brokenDrm[window.location.hostname] = true;
await settings.saveWithoutReload();
this.uiVisible = false;
}
}
}
</script>
<style lang="scss" src="../res/css/uwui-base.scss"></style>
<style lang="scss" src="../res/css/uwui-base.scss" scoped></style>
<style lang="scss" scoped>
@import '../res/css/uwui-base.scss';
@import '../res/css/colors.scss';
// @import '../res/css/font/overpass.css';
// @import '../res/css/font/overpass-mono.css';
@import '../res/css/font/overpass.css';
@import '../res/css/font/overpass-mono.css';
@import '../res/css/common.scss';
.uw-ultrawidify-container-root {
* {
font-family: 'Overpass', 'Segoe UI';
pointer-events: all;
}
pointer-events: auto;
.vspc {
height: 0.5em;
display: block;
}
// .relative-wrapper {
// position: relative;
// width: 100%;
// height: 100%;
// }
.uw-hover {
position: absolute;
top: 5%;
left: 5%;
width: 90%;
height: 90%;
color: #ddd;
background-color: rgba(84, 8, 8, 0.786);
top: 10%;
left: 10%;
width: 100px;
height: 100px;
color: #fff;
background-color: #000;
pointer-events: auto;
overflow-y: scroll;
z-index: 999999999999999999;
}
.uw-hover:hover {
background-color: #f00;
}
p, h1, h2, h3 {
margin: 0.75em;
display: block;
// display: inline-block;
}
.popup-panel {
position: absolute;
h1 {
color: #fff;
text-align: center;
}
top: 10%;
left: 10%;
h2, h3 {
color: #fa6;
text-align: center;
}
z-index: 999999999999999999;
i {
display: block !important;
padding: 4px 8px;
font-family: 'Overpass Mono', monospace;
background-color: rgba(11,11,11,0.75);
font-size: 0.8em;
}
width: 2500px;
height: 1200px;
max-width: 80%;
max-height: 80%;
b {
color: #fff;
}
pointer-events: all !important;
a {
cursor: pointer;
}
background-color: rgba(0,0,0,0.69);
color: #fff;
p {
font-size: 16px;
}
h1 {
font-size: 36px;
}
sup {
vertical-align: super;
font-size: smaller;
}
.text {
flex-grow: 1;
flex-shrink: 0;
max-width: 960px;
min-width: 420px;
}
.image-examples {
flex-grow: auto;
flex-shrink: 1;
min-width: 720px;
img {
width: 100%;
}
}
ul {
list-style-type: disc;
list-style-position: inside;
}
ol {
list-style-type: decimal;
list-style-position: inside;
}
ul ul, ol ul {
list-style-type: circle;
list-style-position: inside;
margin-left: 15px;
}
ol ol, ul ol {
list-style-type: lower-latin;
list-style-position: inside;
margin-left: 15px;
}
li {
margin-top: 16px;
line-height: 16px;
}
}
.pad {
padding: 16px;
height: 100%;
width: 100%;
// padding-bottom: 10%;
overflow-y: auto;
.tab {
display: block;
height: 42px;
font-size: 2.5rem;
background: rgb(87, 54, 26);
}
.tab:hover {
background-color: #f00;
}
}
.flex-row {
justify-content: space-around;
pre {
white-space: pre-wrap;
}
}

134
src/ext/UWContent.ts Normal file
View 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
View 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);
}
}

View File

@ -1,27 +1,27 @@
import VideoAlignment from '../../common/enums/video-alignment.enum';
import Stretch from '../../common/enums/stretch.enum';
import ExtensionMode from '../../common/enums/extension-mode.enum';
import AspectRatio from '../../common/enums/aspect-ratio.enum';
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
import VideoAlignmentType from '../../common/enums/VideoAlignmentType.enum';
import StretchType from '../../common/enums/StretchType.enum';
import ExtensionMode from '../../common/enums/ExtensionMode.enum';
import AspectRatioType from '../../common/enums/AspectRatioType.enum';
import CropModePersistence from '../../common/enums/CropModePersistence.enum';
var ActionList = {
'set-ar': {
name: 'Set aspect ratio',
args: [{
name: 'Automatic',
arg: AspectRatio.Automatic,
arg: AspectRatioType.Automatic,
},{
name: 'Fit width',
arg: AspectRatio.FitWidth,
arg: AspectRatioType.FitWidth,
},{
name: 'Fit height',
arg: AspectRatio.FitHeight,
arg: AspectRatioType.FitHeight,
},{
name: 'Reset',
arg: AspectRatio.Reset,
arg: AspectRatioType.Reset,
},{
name: 'Manually specify ratio',
arg: AspectRatio.Fixed,
arg: AspectRatioType.Fixed,
customArg: true,
customSetter: (value) => {
const [width, height] = value.split(':');
@ -70,33 +70,33 @@ var ActionList = {
name: 'Set stretch',
args: [{
name: 'Normal',
arg: Stretch.NoStretch
arg: StretchType.NoStretch
},{
name: 'Basic',
arg: Stretch.Basic,
arg: StretchType.Basic,
},{
name: 'Hybrid',
arg: Stretch.Hybrid,
arg: StretchType.Hybrid,
},{
name: 'Thin borders',
arg: Stretch.Conditional,
arg: StretchType.Conditional,
},{
name: 'Fixed (source)',
arg: Stretch.FixedSource,
arg: StretchType.FixedSource,
customArg: true,
scopes: {
page: true,
}
},{
name: 'Fixed (displayed)',
arg: Stretch.Fixed,
arg: StretchType.Fixed,
customArg: true,
scopes: {
page: true,
}
},{
name: 'Default',
arg: Stretch.Default,
arg: StretchType.Default,
scopes: {
site: true
}
@ -111,16 +111,16 @@ var ActionList = {
name: 'Set video alignment',
args: [{
name: 'Left',
arg: VideoAlignment.Left,
arg: VideoAlignmentType.Left,
},{
name: 'Center',
arg: VideoAlignment.Center,
arg: VideoAlignmentType.Center,
},{
name: 'Right',
arg: VideoAlignment.Right
arg: VideoAlignmentType.Right
},{
name: 'Default',
arg: VideoAlignment.Default,
arg: VideoAlignmentType.Default,
scopes: {
site: true,
}
@ -179,7 +179,7 @@ var ActionList = {
page: true,
}
},
'set-extension-mode': {
'set-ExtensionMode': {
name: 'Set extension mode',
args: [{
name: 'Enable',

View File

@ -1,8 +1,8 @@
// How to use:
// version: {ExtensionConf object, but only properties that get overwritten}
import Stretch from '../../common/enums/stretch.enum';
import ExtensionMode from '../../common/enums/extension-mode.enum';
import VideoAlignment from '../../common/enums/video-alignment.enum';
import StretchType from '../../common/enums/StretchType.enum';
import ExtensionMode from '../../common/enums/ExtensionMode.enum';
import VideoAlignmentType from '../../common/enums/VideoAlignmentType.enum';
const ExtensionConfPatch = [
{
@ -267,7 +267,7 @@ const ExtensionConfPatch = [
label: '4:3 stretch (src)',
cmd: [{
action: 'set-stretch',
arg: Stretch.FixedSource,
arg: StretchType.FixedSource,
customArg: 1.33,
}],
scopes: {
@ -284,7 +284,7 @@ const ExtensionConfPatch = [
label: '16:9 stretch (src)',
cmd: [{
action: 'set-stretch',
arg: Stretch.FixedSource,
arg: StretchType.FixedSource,
customArg: 1.77,
}],
scopes: {
@ -309,8 +309,8 @@ const ExtensionConfPatch = [
autoar: ExtensionMode.Enabled,
autoarFallback: ExtensionMode.Enabled,
override: true, // ignore value localStorage in favour of this
stretch: Stretch.Default,
videoAlignment: VideoAlignment.Default,
stretch: StretchType.Default,
videoAlignment: VideoAlignmentType.Default,
keyboardShortcutsEnabled: ExtensionMode.Default,
DOM: {
player: {
@ -339,8 +339,8 @@ const ExtensionConfPatch = [
autoar: ExtensionMode.Enabled,
override: false,
type: 'community',
stretch: Stretch.Default,
videoAlignment: VideoAlignment.Default,
stretch: StretchType.Default,
videoAlignment: VideoAlignmentType.Default,
keyboardShortcutsEnabled: ExtensionMode.Default,
arPersistence: true, // persist aspect ratio between different videos
autoarPreventConditions: { // prevents autoar on following conditions

View File

@ -1,16 +1,17 @@
import Debug from './Debug';
import currentBrowser from './BrowserDetect';
import VideoAlignment from '../../common/enums/video-alignment.enum';
import Stretch from '../../common/enums/stretch.enum';
import ExtensionMode from '../../common/enums/extension-mode.enum';
import AntiGradientMode from '../../common/enums/anti-gradient-mode.enum';
import AspectRatio from '../../common/enums/aspect-ratio.enum';
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
import VideoAlignmentType from '../../common/enums/VideoAlignmentType.enum';
import StretchType from '../../common/enums/StretchType.enum';
import ExtensionMode from '../../common/enums/ExtensionMode.enum';
import AntiGradientMode from '../../common/enums/AntiGradientMode.enum';
import AspectRatioType from '../../common/enums/AspectRatioType.enum';
import CropModePersistence from '../../common/enums/CropModePersistence.enum';
import SettingsInterface from '../../common/interfaces/SettingsInterface';
if(Debug.debug)
console.log("Loading: ExtensionConf.js");
var ExtensionConf = {
const ExtensionConf: SettingsInterface = {
arDetect: {
disabledReason: "", // if automatic aspect ratio has been disabled, show reason
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)
cmd: [{
action: 'set-ar',
arg: AspectRatio.Automatic,
arg: AspectRatioType.Automatic,
persistent: false, // optional, false by default. If true, change doesn't take effect immediately.
// Instead, this action saves stuff to settings
}],
@ -211,7 +212,7 @@ var ExtensionConf = {
label: 'Reset',
cmd: [{
action: 'set-ar',
arg: AspectRatio.Reset,
arg: AspectRatioType.Reset,
}],
scopes: {
page: {
@ -237,7 +238,7 @@ var ExtensionConf = {
label: 'Fit width',
cmd: [{
action: 'set-ar',
arg: AspectRatio.FitWidth,
arg: AspectRatioType.FitWidth,
}],
scopes: {
page: {
@ -263,7 +264,7 @@ var ExtensionConf = {
label: 'Fit height',
cmd: [{
action: 'set-ar',
arg: AspectRatio.FitHeight
arg: AspectRatioType.FitHeight
}],
scopes: {
page: {
@ -290,7 +291,7 @@ var ExtensionConf = {
label: '16:9',
cmd: [{
action: 'set-ar',
arg: AspectRatio.Fixed,
arg: AspectRatioType.Fixed,
customArg: 1.78,
}],
scopes: {
@ -318,7 +319,7 @@ var ExtensionConf = {
label: '21:9',
cmd: [{
action: 'set-ar',
arg: AspectRatio.Fixed,
arg: AspectRatioType.Fixed,
customArg: 2.39
}],
scopes: {
@ -346,7 +347,7 @@ var ExtensionConf = {
label: '18:9',
cmd: [{
action: 'set-ar',
arg: AspectRatio.Fixed,
arg: AspectRatioType.Fixed,
customArg: 2.0,
}],
scopes: {
@ -373,7 +374,7 @@ var ExtensionConf = {
label: 'Never persist',
cmd: [{
action: 'set-ar-persistence',
arg: CropModePersistence.Never,
arg: CropModePersistence.Disabled,
}],
scopes: {
site: {
@ -547,7 +548,7 @@ var ExtensionConf = {
label: 'Don\'t stretch',
cmd: [{
action: 'set-stretch',
arg: Stretch.NoStretch,
arg: StretchType.NoStretch,
}],
scopes: {
global: {
@ -572,7 +573,7 @@ var ExtensionConf = {
label: 'Basic stretch',
cmd: [{
action: 'set-stretch',
arg: Stretch.Basic,
arg: StretchType.Basic,
}],
scopes: {
global: {
@ -597,7 +598,7 @@ var ExtensionConf = {
label: 'Hybrid stretch',
cmd: [{
action: 'set-stretch',
arg: Stretch.Hybrid,
arg: StretchType.Hybrid,
}],
scopes: {
global: {
@ -622,7 +623,7 @@ var ExtensionConf = {
label: 'Thin borders only',
cmd: [{
action: 'set-stretch',
arg: Stretch.Conditional,
arg: StretchType.Conditional,
}],
scopes: {
global: {
@ -647,7 +648,7 @@ var ExtensionConf = {
label: 'Default',
cmd: [{
action: 'set-stretch',
arg: Stretch.Default,
arg: StretchType.Default,
}],
scopes: {
site: {
@ -660,7 +661,7 @@ var ExtensionConf = {
label: '4:3 stretch (src)',
cmd: [{
action: 'set-stretch',
arg: Stretch.FixedSource,
arg: StretchType.FixedSource,
customArg: 1.33,
}],
scopes: {
@ -677,7 +678,7 @@ var ExtensionConf = {
label: '16:9 stretch (src)',
cmd: [{
action: 'set-stretch',
arg: Stretch.FixedSource,
arg: StretchType.FixedSource,
customArg: 1.77,
}],
scopes: {
@ -698,7 +699,7 @@ var ExtensionConf = {
label: 'Left',
cmd: [{
action: 'set-alignment',
arg: VideoAlignment.Left,
arg: VideoAlignmentType.Left,
}],
scopes: {
global: {
@ -720,7 +721,7 @@ var ExtensionConf = {
label: 'Center',
cmd: [{
action: 'set-alignment',
arg: VideoAlignment.Center,
arg: VideoAlignmentType.Center,
}],
scopes: {
global: {
@ -742,7 +743,7 @@ var ExtensionConf = {
label: 'Right',
cmd: [{
action: 'set-alignment',
arg: VideoAlignment.Right
arg: VideoAlignmentType.Right
}],
scopes: {
global: {
@ -764,7 +765,7 @@ var ExtensionConf = {
label: 'Default',
cmd: [{
action: 'set-alignment',
arg: VideoAlignment.Default
arg: VideoAlignmentType.Default
}],
scopes: {
site: {
@ -780,7 +781,7 @@ var ExtensionConf = {
name: 'Enable extension',
label: 'Enable',
cmd: [{
action: 'set-extension-mode',
action: 'set-ExtensionMode',
arg: ExtensionMode.Enabled,
persistent: true,
}],
@ -796,7 +797,7 @@ var ExtensionConf = {
name: 'Enable extension on whitelisted sites only',
label: 'On whitelist only',
cmd: [{
action: 'set-extension-mode',
action: 'set-ExtensionMode',
arg: ExtensionMode.Whitelist,
persistent: true,
}],
@ -809,7 +810,7 @@ var ExtensionConf = {
name: 'Extension mode: use default settings',
label: 'Default',
cmd: [{
action: 'set-extension-mode',
action: 'set-ExtensionMode',
arg: ExtensionMode.Default,
persistent: true,
}],
@ -822,7 +823,7 @@ var ExtensionConf = {
name: 'Disable extension',
label: 'Disable',
cmd: [{
action: 'set-extension-mode',
action: 'set-ExtensionMode',
arg: ExtensionMode.Disabled,
persistent: true,
}],
@ -1006,8 +1007,8 @@ var ExtensionConf = {
autoarFallback: currentBrowser.firefox ? // if autoAr fails, try fallback mode?
ExtensionMode.Enabled : // Options same as in autoar.
ExtensionMode.Disabled, // if autoar is disabled, this setting is irrelevant
stretch: Stretch.NoStretch, // Default stretch mode.
videoAlignment: VideoAlignment.Center, // Video alignment
stretch: StretchType.NoStretch, // Default stretch mode.
videoAlignment: VideoAlignmentType.Center, // Video alignment
keyboardShortcutsEnabled: ExtensionMode.Enabled,
},
"www.youtube.com" : {
@ -1017,8 +1018,8 @@ var ExtensionConf = {
override: false, // ignore value localStorage in favour of this
type: 'official', // is officially supported? (Alternatives are 'community' and 'user-defined')
actions: null, // overrides global keyboard shortcuts and button configs. Is array, is optional.
stretch: Stretch.Default,
videoAlignment: VideoAlignment.Default,
stretch: StretchType.Default,
videoAlignment: VideoAlignmentType.Default,
keyboardShortcutsEnabled: ExtensionMode.Default,
DOM: {
player: {
@ -1035,8 +1036,8 @@ var ExtensionConf = {
autoar: ExtensionMode.Enabled,
override: false,
type: 'official',
stretch: Stretch.Default,
videoAlignment: VideoAlignment.Default,
stretch: StretchType.Default,
videoAlignment: VideoAlignmentType.Default,
keyboardShortcutsEnabled: ExtensionMode.Default,
arPersistence: true, // persist aspect ratio between different videos
"DOM": {
@ -1054,8 +1055,8 @@ var ExtensionConf = {
autoar: ExtensionMode.Enabled,
override: false,
type: 'community',
stretch: Stretch.Default,
videoAlignment: VideoAlignment.Default,
stretch: StretchType.Default,
videoAlignment: VideoAlignmentType.Default,
keyboardShortcutsEnabled: ExtensionMode.Default,
arPersistence: true, // persist aspect ratio between different videos
DOM: {
@ -1074,8 +1075,8 @@ var ExtensionConf = {
autoar: ExtensionMode.Enabled,
override: true,
type: 'official',
stretch: Stretch.Default,
videoAlignment: VideoAlignment.Default,
stretch: StretchType.Default,
videoAlignment: VideoAlignmentType.Default,
keyboardShortcutsEnabled: ExtensionMode.Default,
DOM: {
player: {
@ -1117,8 +1118,8 @@ var ExtensionConf = {
autoar:ExtensionMode.Enabled,
override: false,
type: 'testing',
stretch: Stretch.Default,
videoAlignment: VideoAlignment.Default,
stretch: StretchType.Default,
videoAlignment: VideoAlignmentType.Default,
keyboardShortcutsEnabled: ExtensionMode.Default,
DOM: {
player: {
@ -1134,8 +1135,8 @@ var ExtensionConf = {
autoar: ExtensionMode.Enabled,
override: false,
type: 'testing',
stretch: Stretch.Default,
videoAlignment: VideoAlignment.Default,
stretch: StretchType.Default,
videoAlignment: VideoAlignmentType.Default,
keyboardShortcutsEnabled: ExtensionMode.Default,
DOM: {
player: {

View File

@ -1,20 +1,37 @@
import Debug from '../conf/Debug';
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'){
console.info("Loading 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) {
this.logger = pageInfo.logger;
this.pageInfo = pageInfo;
this.settings = pageInfo.settings;
this.inputs = ['input', 'select', 'button', 'textarea'];
this.keyboardLocalDisabled = false;
}
init() {
@ -105,7 +122,7 @@ class ActionHandler {
}
// 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
document.addEventListener('keydown', this );
document.addEventListener('keyup', this );
@ -168,7 +185,6 @@ class ActionHandler {
this.logger.resume(); // undisable
this.logger.log('info', 'keyboard', "[ActionHandler::preventAction] Testing whether we're in a textbox or something. Detailed rundown of conditions:\n" +
"is full screen? (yes->allow):", PlayerData.isFullScreen(),
"\nis tag one of defined inputs? (yes->prevent):", this.inputs.indexOf(activeElement.tagName.toLocaleLowerCase()) !== -1,
"\nis role = textbox? (yes -> prevent):", activeElement.getAttribute("role") === "textbox",
"\nis type === 'text'? (yes -> prevent):", activeElement.getAttribute("type") === "text",
@ -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) {
return true;
}
@ -240,7 +250,7 @@ class ActionHandler {
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);
const isLatin = event.key ? this.isLatin(event.key) : true;
@ -276,9 +286,9 @@ class ActionHandler {
} else if (cmd.action === "set-alignment") {
this.settings.active.sites[site].videoAlignment = cmd.arg;
} else if (cmd.action === "set-extension-mode") {
this.settings.active.sites[site].status = cmd.arg;
this.settings.active.sites[site].mode = cmd.arg;
} 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') {
this.settings.active.sites[site].keyboardShortcutsEnabled = cmd.arg;
} else if (cmd.action === 'set-ar-persistence') {
@ -323,9 +333,9 @@ class ActionHandler {
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);
videoData.panHandler(event);
videoData?.panHandler(event);
this.execAction(this.mouseMoveActions, event, videoData)
}

View File

@ -1,42 +1,112 @@
import currentBrowser from '../conf/BrowserDetect';
import { decycle } from 'json-cyclic';
import Comms from './comms/Comms';
import BrowserDetect from '../conf/BrowserDetect';
import { browser } from 'webextension-polyfill-ts';
if (process.env.CHANNEL !== 'stable'){
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.uwInstance = options?.uwInstance;
}
static saveConfig(conf) {
static saveConfig(conf: LoggerConfig) {
if (process.env.CHANNEL === 'dev') {
console.info('Saving logger conf:', conf)
}
if (currentBrowser.firefox || currentBrowser.edge) {
return browser.storage.local.set( {'uwLogger': JSON.stringify(conf)});
} else if (currentBrowser.chrome) {
return chrome.storage.local.set( {'uwLogger': JSON.stringify(conf)});
}
browser.storage.local.set( {'uwLogger': JSON.stringify(conf)});
}
static syncConfig(callback) {
const br = currentBrowser.firefox ? browser : chrome;
br.storage.onChanged.addListener( (changes, area) => {
static syncConfig(callback: (x) => void) {
browser.storage.onChanged.addListener( (changes, area) => {
if (changes.uwLogger) {
const newLoggerConf = JSON.parse(changes.uwLogger.newValue)
if (process.env.CHANNEL === 'dev') {
console.info('Logger settings reloaded. New conf:', conf);
console.info('Logger settings reloaded. New conf:', newLoggerConf);
}
callback(newLoggerConf);
}
@ -46,17 +116,13 @@ class Logger {
static async getConfig() {
let ret;
if (currentBrowser.firefox) {
// if (BrowserDetect.firefox) {
ret = await browser.storage.local.get('uwLogger');
} else if (currentBrowser.chrome) {
ret = await new Promise( (resolve, reject) => {
chrome.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));
});
}
// } else if (BrowserDetect.anyChromium) {
// ret = await new Promise( (resolve, reject) => {
// browser.storage.local.get('uwLogger', (res) => resolve(res));
// });
// }
if (process.env.CHANNEL === 'dev') {
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
// and doesn't get ignored even if the rest of the conf gets
// loaded from browser storage
@ -101,9 +167,7 @@ class Logger {
this.temp_disable = false;
this.stopTime = this.conf.timeout ? performance.now() + (this.conf.timeout * 1000) : undefined;
const br = currentBrowser.firefox ? browser : chrome;
br.storage.onChanged.addListener( (changes, area) => {
browser.storage.onChanged.addListener( (changes, area) => {
if (process.env.CHANNEL === 'dev') {
if (!changes.uwLogger) {
// console.info('[Logger::<storage/on change> No new logger settings!');
@ -131,7 +195,7 @@ class Logger {
}
clear() {
this.log = [];
this.history = [];
this.startTime = performance.now();
this.stopTime = this.conf.timeout ? performance.now() + (this.conf.timeout * 1000) : undefined;
}
@ -142,9 +206,9 @@ class Logger {
Logger.saveConfig(conf);
}
async getSaved() {
return Logger.getSaved();
}
// async getSaved() {
// return Logger.getSaved();
// }
// allow syncing of start times between bg and page scripts.
@ -174,7 +238,7 @@ class Logger {
getFileLogJSONString() {
return {
site: window && window.location,
log: JSON.toString(this.history),
log: JSON.stringify(this.history),
}
}
@ -213,7 +277,7 @@ class Logger {
parseStack() {
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,
// since that is useless because minification/webpack
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) {
return false;
}
@ -437,7 +501,7 @@ class Logger {
let ts = performance.now();
let secondMark = ts - 1000;
let halfSecondMark = ts - 500;
let i = this.history.length();
let i = this.history.length;
// correct ts _after_ secondMark and halfSecondMark were determined
if (ts <= this.history[this.history.length - 1]) {

View File

@ -4,7 +4,7 @@ class ObjectCopy {
static addNew(current, newValues){
// clone target
var out = JSON.parse(JSON.stringify(newValues));
let out = JSON.parse(JSON.stringify(newValues));
if(! current) {
if(Debug.debug) {
@ -14,7 +14,7 @@ class ObjectCopy {
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[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
// sites don't get forgotten)
for(var k in current) {
for(let k in current) {
if (! out[k]) {
out[k] = current[k];
}
@ -47,7 +47,7 @@ class ObjectCopy {
}
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[k] !== undefined) {
// Types and constructors of objects must match. If they don't, we always use the new value.

View File

@ -1,19 +1,41 @@
import Debug from '../conf/Debug';
import currentBrowser from '../conf/BrowserDetect';
import ExtensionConf from '../conf/ExtensionConf';
import ExtensionMode from '../../common/enums/extension-mode.enum';
import ObjectCopy from '../lib/ObjectCopy';
import Stretch from '../../common/enums/stretch.enum';
import VideoAlignment from '../../common/enums/video-alignment.enum';
import ExtensionMode from '../../common/enums/ExtensionMode.enum';
import ObjectCopy from './ObjectCopy';
import StretchType from '../../common/enums/StretchType.enum';
import VideoAlignmentType from '../../common/enums/VideoAlignmentType.enum';
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 Logger from './Logger';
import SettingsInterface from '../../common/interfaces/SettingsInterface';
import { browser } from 'webextension-polyfill-ts';
if(process.env.CHANNEL !== 'stable'){
console.info("Loading 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) {
// Options: activeSettings, updateCallback, logger
@ -23,14 +45,8 @@ class Settings {
this.active = options?.activeSettings ?? undefined;
this.default = ExtensionConf;
this.default['version'] = this.getExtensionVersion();
this.useSync = false;
this.version = undefined;
if (currentBrowser.firefox) {
browser.storage.onChanged.addListener((changes, area) => {this.storageChangeListener(changes, area)});
} else if (currentBrowser.chrome) {
chrome.storage.onChanged.addListener((changes, area) => {this.storageChangeListener(changes, area)});
}
browser.storage.onChanged.addListener((changes, area) => {this.storageChangeListener(changes, area)});
}
storageChangeListener(changes, area) {
@ -60,14 +76,10 @@ class Settings {
}
}
static getExtensionVersion() {
if (currentBrowser.firefox) {
return browser.runtime.getManifest().version;
} else if (currentBrowser.chrome) {
return chrome.runtime.getManifest().version;
}
static getExtensionVersion(): string {
return browser.runtime.getManifest().version;
}
getExtensionVersion() {
getExtensionVersion(): string {
return Settings.getExtensionVersion();
}
@ -96,7 +108,10 @@ class Settings {
// also, the fourth digit can start with a letter.
// versions that start with a letter are ranked lower than
// 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;
}
@ -268,17 +283,7 @@ class Settings {
async get() {
let ret;
if (currentBrowser.firefox) {
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));
});
}
ret = await browser.storage.local.get('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;
}
if (sites[site].stretch === undefined) {
sites[site].stretch = Stretch.Default;
sites[site].stretch = StretchType.Default;
}
if (sites[site].videoAlignment === undefined) {
sites[site].videoAlignment = VideoAlignment.Default;
sites[site].videoAlignment = VideoAlignmentType.Default;
}
if (sites[site].keyboardShortcutsEnabled === undefined) {
sites[site].keyboardShortcutsEnabled = ExtensionMode.Default;
@ -312,7 +317,7 @@ class Settings {
}
}
async set(extensionConf, options) {
async set(extensionConf, options?) {
if (!options || !options.forcePreserveVersion) {
extensionConf.version = this.version;
}
@ -321,11 +326,7 @@ class Settings {
this.logger?.log('info', 'settings', "[Settings::set] setting new settings:", extensionConf)
if (BrowserDetect.firefox) {
return browser.storage.local.set( {'uwSettings': JSON.stringify(extensionConf)});
} else {
return chrome.storage.local.set( {'uwSettings': JSON.stringify(extensionConf)});
}
return browser.storage.local.set( {'uwSettings': JSON.stringify(extensionConf)});
}
async setActive(activeSettings) {
@ -336,7 +337,7 @@ class Settings {
this.active[prop] = value;
}
async save(options) {
async save(options?) {
if (Debug.debug && Debug.storage) {
console.log("[Settings::save] Saving active settings:", this.active);
}
@ -389,7 +390,7 @@ class Settings {
return this.active.actions;
}
getExtensionMode(site) {
getExtensionMode(site?: string) {
if (!site) {
site = window.location.hostname;
@ -494,7 +495,7 @@ class Settings {
return this.canStartExtension(site);
}
canStartAutoAr(site) {
canStartAutoAr(site?: string) {
// 'site' argument is only ever used when calling this function recursively for debugging
if (!site) {
site = window.location.hostname;
@ -539,13 +540,13 @@ class Settings {
}
}
getDefaultOption(option) {
getDefaultOption(option?) {
const allDefault = {
mode: ExtensionMode.Default,
autoar: ExtensionMode.Default,
autoarFallback: ExtensionMode.Default,
stretch: Stretch.Default,
videoAlignment: VideoAlignment.Default,
stretch: StretchType.Default,
videoAlignment: VideoAlignmentType.Default,
};
if (!option || allDefault[option] === undefined) {
@ -565,7 +566,7 @@ class Settings {
}
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;
}
@ -573,7 +574,7 @@ class Settings {
}
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;
}
@ -582,7 +583,7 @@ class Settings {
}
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;
}

View File

@ -1,3 +1,3 @@
export async function sleep(timeout) {
return new Promise( (resolve, reject) => setTimeout(() => resolve(), timeout));
return new Promise( (resolve, reject) => setTimeout(() => resolve(null), timeout));
}

View File

@ -6,12 +6,56 @@ import EdgeDetectPrimaryDirection from './edge-detect/enums/EdgeDetectPrimaryDir
import EdgeDetectQuality from './edge-detect/enums/EdgeDetectQualityEnum';
import GuardLine from './GuardLine';
// import DebugCanvas from './DebugCanvas';
import VideoAlignment from '../../../common/enums/video-alignment.enum';
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
import {sleep} from '../../lib/Util';
import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum';
import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
import {sleep} from '../Util';
import BrowserDetect from '../../conf/BrowserDetect';
import Logger from '../Logger';
import VideoData from '../video-data/VideoData';
import Settings from '../Settings';
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){
this.logger = videoData.logger;
@ -19,34 +63,18 @@ class ArDetector {
this.video = videoData.video;
this.settings = videoData.settings;
this.setupTimer = null;
this.sampleCols = [];
this.canFallback = true;
this.fallbackMode = false;
this.blackLevel = this.settings.active.arDetect.blackbar.blackLevel;
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
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}`);
}
setManualTick(manualTick) {
this._manualTicks = manualTick;
this.manualTickEnabled = manualTick;
}
tick() {
@ -68,19 +96,19 @@ class ArDetector {
}
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.stop();
}
setup(cwidth, cheight){
setup(cwidth?: number, cheight?: number){
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
// 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
//
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.scheduleInitRestart();
@ -133,16 +161,16 @@ class ArDetector {
// [2] determine places we'll use to sample our main frame
//
var ncol = this.settings.active.arDetect.sampling.staticCols;
var nrow = this.settings.active.arDetect.sampling.staticRows;
let ncol = this.settings.active.arDetect.sampling.staticCols;
let nrow = this.settings.active.arDetect.sampling.staticRows;
var colSpacing = this.canvas.width / ncol;
var rowSpacing = (this.canvas.height << 2) / nrow;
let colSpacing = this.canvas.width / ncol;
let rowSpacing = (this.canvas.height << 2) / nrow;
this.sampleLines = [];
this.sampleCols = [];
for(var i = 0; i < ncol; i++){
for(let i = 0; i < ncol; i++){
if(i < ncol - 1)
this.sampleCols.push(Math.round(colSpacing * i));
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)
this.sampleLines.push(Math.round(rowSpacing * i));
else{
@ -173,7 +201,7 @@ class ArDetector {
//
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;
} catch (e) {
this.canDoFallbackMode = false;
@ -187,7 +215,7 @@ class ArDetector {
this.resetBlackLevel();
// if we're restarting ArDetect, we need to do this in order to force-recalculate aspect ratio
this.conf.resizer.setLastAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
this.conf.resizer.setLastAr({type: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
this.canvasImageDataRowLength = cwidth << 2;
this.noLetterboxCanvasReset = false;
@ -212,9 +240,9 @@ class ArDetector {
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
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
// paused state timeout to finish.
if ( (!this._manualTicks && this.canTriggerFrameCheck(lastFrameCheckStartTime)) || this._nextTick) {
if ( (!this.manualTickEnabled && this.canTriggerFrameCheck(lastFrameCheckStartTime)) || this._nextTick) {
this._nextTick = false;
lastFrameCheckStartTime = Date.now();
@ -344,7 +372,7 @@ class ArDetector {
}
scheduleInitRestart(timeout, force_reset){
scheduleInitRestart(timeout?: number, force_reset?: boolean){
if(! timeout){
timeout = 100;
}
@ -353,7 +381,7 @@ class ArDetector {
clearTimeout(this.setupTimer);
}
var ths = this;
let ths = this;
this.setupTimer = setTimeout(function(){
ths.setupTimer = null;
try{
@ -391,7 +419,7 @@ class ArDetector {
}
getTimeout(baseTimeout, startTime){
var execTime = (performance.now() - startTime);
let execTime = (performance.now() - startTime);
return baseTimeout;
}
@ -447,12 +475,12 @@ class ArDetector {
// letterbox also needs to be corrected:
// letterbox += [video.zoomedHeight] - [video.unzoomedHeight]
var vbr = this.video.getBoundingClientRect();
let vbr = this.video.getBoundingClientRect();
zoomFactor = 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 ){
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
// check if aspect ratio is changed:
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
// že nastavili.
//
// 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)
arDiff = -arDiff;
@ -490,7 +518,7 @@ class ArDetector {
// ali je sprememba v mejah dovoljenega? Če da -> fertik
// 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){
this.logger.log('info', 'arDetect', `%c[ArDetect::processAr] <@${this.arid}> Aspect ratio change denied — diff %: ${arDiff_percent}`, "background: #740; color: #fa2");
@ -500,12 +528,12 @@ class ArDetector {
}
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) {
if (ArrayBuffer.transfer) {
ArrayBuffer.transfer(id, 0);
if ((ArrayBuffer as any).transfer) {
(ArrayBuffer as any).transfer(id, 0);
}
id = undefined;
}
@ -521,7 +549,7 @@ class ArDetector {
this.init();
}
var startTime = performance.now();
let startTime = performance.now();
let sampleCols = this.sampleCols.slice(0);
//
@ -562,9 +590,9 @@ class ArDetector {
let newCanvasWidth = window.innerHeight * (this.video.videoWidth / this.video.videoHeight);
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);
} else if (this.conf.resizer.videoAlignment === VideoAlignment.Left) {
} else if (this.conf.resizer.videoAlignment === VideoAlignmentType.Left) {
this.canvasDrawWindowHOffset = 0;
} else {
this.canvasDrawWindowHOffset = window.innerWidth - newCanvasWidth;
@ -579,7 +607,7 @@ class ArDetector {
this.fallbackMode = true;
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) {
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
@ -611,7 +639,7 @@ class ArDetector {
// da je letterbox izginil.
// If we don't detect letterbox, we reset aspect ratio to aspect ratio of the video file. The aspect ratio could
// have been corrected manually. It's also possible that letterbox (that was there before) disappeared.
this.conf.resizer.updateAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
this.conf.resizer.updateAr({type: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
this.guardLine.reset();
this.noLetterboxCanvasReset = true;
@ -644,14 +672,16 @@ class ArDetector {
// otherwise we continue. We add blackbar violations to the list of the cols
// we'll sample and sort them
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
// (since the new letterbox edge isn't present in our sample due to technical
// limitations)
if (this.fallbackMode && guardLineOut.blackbarFail) {
this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
this.conf.resizer.setAr({type: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
this.guardLine.reset();
this.noLetterboxCanvasReset = true;
@ -671,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.
try{
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){
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();
}
triggerTimeout = this.getTimeout(baseTimeout, startTime);
this.scheduleFrameCheck(triggerTimeout);
this.clearImageData(imageData);
return;
}
@ -696,20 +724,20 @@ class ArDetector {
// 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);
if (edgePost.status !== EdgeStatus.AR_KNOWN){
if (edgePost.status !== EdgeStatus.ARKnown){
// rob ni bil zaznan, zato ne naredimo ničesar.
// no edge was detected. Let's leave things as they were
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Edge wasn't detected with findBars`, "color: #fa3", edgePost, "EdgeStatus.AR_KNOWN:", EdgeStatus.AR_KNOWN);
this.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);
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");
@ -741,14 +769,14 @@ class ArDetector {
this.logger.log('error', 'arDetect', `%c[ArDetect::frameCheck] There was a problem setting blackbar. Doing nothing. Error:`, e);
try {
this.guardline.reset();
this.guardLine.reset();
} catch (e) {
// no guardline, no bigge
}
// WE DO NOT RESET ASPECT RATIO HERE IN CASE OF PROBLEMS, CAUSES UNWARRANTED RESETS:
// (eg. here: https://www.youtube.com/watch?v=nw5Z93Yt-UQ&t=410)
//
// this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
// this.conf.resizer.setAr({type: AspectRatioType.Automatic, ratio: this.getDefaultAr()});
}
this.clearImageData(imageData);
@ -780,7 +808,7 @@ class ArDetector {
let cumulativeValue = 0;
let blackPixelCount = 0;
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
@ -788,7 +816,7 @@ class ArDetector {
let rowMax = new Array(rows).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) {
@ -902,7 +930,7 @@ class ArDetector {
// 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_g = colOffset_r + 1;
colOffset_b = colOffset_r + 2;
@ -938,8 +966,8 @@ class ArDetector {
}
var _ard_console_stop = "background: #000; color: #f41";
var _ard_console_start = "background: #000; color: #00c399";
var _ard_console_change = "background: #000; color: #ff8";
let _ard_console_stop = "background: #000; color: #f41";
let _ard_console_start = "background: #000; color: #00c399";
let _ard_console_change = "background: #000; color: #ff8";
export default ArDetector;

View File

@ -1,13 +1,33 @@
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 {
blackbar: GuardLineBar;
imageBar: GuardLineBar;
aard: ArDetector;
settings: Settings;
blackbarThreshold: number;
imageThreshold: number;
// ardConf — reference to ArDetector that has current GuardLine instance
constructor(ardConf){
constructor (ardConf){
this.blackbar = {top: undefined, bottom: undefined};
this.imageBar = {top: undefined, bottom: undefined};
this.conf = ardConf;
this.aard = ardConf;
this.settings = ardConf.settings;
}
@ -28,12 +48,12 @@ class GuardLine {
}
setBlackbar(bbconf){
var bbTop = bbconf.top - this.settings.active.arDetect.guardLine.edgeTolerancePx;
var bbBottom = bbconf.bottom + this.settings.active.arDetect.guardLine.edgeTolerancePx;
let bbTop = bbconf.top - 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
// 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}
}
@ -51,12 +71,12 @@ class GuardLine {
check(image, fallbackMode){
// izračunaj enkrat in shrani na objekt
// 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;
// dejansko testiranje
// 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
// 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 {
blackbarFail: false,
@ -95,45 +115,46 @@ class GuardLine {
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 = [];
var offenderCount = -1; // doing it this way means first offender has offenderCount==0. Ez index.
let offenders = [];
let offenderCount = -1; // doing it this way means first offender has offenderCount==0. Ez index.
// TODO: implement logo check.
// preglejmo obe vrstici
// check both rows
let edge_lower, edge_upper;
if(! fallbackMode){
var edge_upper = this.blackbar.top;
var edge_lower = this.blackbar.bottom;
edge_upper = this.blackbar.top;
edge_lower = this.blackbar.bottom;
}
else{
else {
// fallback mode is a bit different
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 |========>>>
rowStart = ((edge_upper * this.conf.canvas.width) << 2) + offset;
rowEnd = rowStart + ( this.conf.canvas.width << 2 ) - (offset * 2);
rowStart = ((edge_upper * this.aard.canvas.width) << 2) + offset;
rowEnd = rowStart + ( this.aard.canvas.width << 2 ) - (offset * 2);
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 {
offenderCount = this._gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount);
}
// <<<=======| checking lower row |========>>>
rowStart = ((edge_lower * this.conf.canvas.width) << 2) + offset;
rowEnd = rowStart + ( this.conf.canvas.width << 2 ) - (offset * 2);
rowStart = ((edge_lower * this.aard.canvas.width) << 2) + offset;
rowEnd = rowStart + ( this.aard.canvas.width << 2 ) - (offset * 2);
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 {
offenderCount = this._gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount);
}
@ -148,19 +169,19 @@ class GuardLine {
return {success: true};
}
var ret = new Array(offenders.length);
for(var o in offenders){
let ret = new Array(offenders.length);
for(let o in offenders){
ret[o] = offenders[o].x + (offenders[o].width * 0.25);
}
return {success: false, offenders: ret};
}
imageCheck(image){
imageCheck(image, fallbackMode?: boolean): ImageCheckResult {
if(!this.imageBar.top || !this.imageBar.bottom)
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.
@ -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
// if(fallbackMode){
// var edge_upper = this.settings.active.arDetect.fallbackMode.noTriggerZonePx;
// var edge_lower = this.conf.canvas.height - this.settings.active.arDetect.fallbackMode.noTriggerZonePx - 1;
// let edge_upper = this.settings.active.arDetect.fallbackMode.noTriggerZonePx;
// let edge_lower = this.conf.canvas.height - this.settings.active.arDetect.fallbackMode.noTriggerZonePx - 1;
// }
// else{
var edge_upper = this.imageBar.top;
var edge_lower = this.imageBar.bottom;
let edge_upper = this.imageBar.top;
let edge_lower = this.imageBar.bottom;
// }
// 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)
// 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)
var successThreshold = (this.conf.canvas.width * this.settings.active.arDetect.guardLine.imageTestThreshold);
var rowStart, rowEnd;
let successThreshold = (this.aard.canvas.width * this.settings.active.arDetect.guardLine.imageTestThreshold);
let rowStart, rowEnd;
// <<<=======| checking upper row |========>>>
rowStart = ((edge_upper * this.conf.canvas.width) << 2) + offset;
rowEnd = rowStart + ( this.conf.canvas.width << 2 ) - (offset * 2);
rowStart = ((edge_upper * this.aard.canvas.width) << 2) + offset;
rowEnd = rowStart + ( this.aard.canvas.width << 2 ) - (offset * 2);
var res = false;
let res = false;
if(Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine){
res = this._ti_debugCheckRow(image, rowStart, rowEnd, successThreshold);
// res = this._ti_debugCheckRow(image, rowStart, rowEnd, successThreshold);
} else {
res = this._ti_checkRow(image, rowStart, rowEnd,successThreshold);
}
if(res)
if (res) {
return {success: true};
}
// <<<=======| 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);
if(Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine){
res = this._ti_debugCheckRow(image, rowStart, rowEnd, successThreshold);
// res = this._ti_debugCheckRow(image, rowStart, rowEnd, successThreshold);
} else {
res = this._ti_checkRow(image, rowStart, rowEnd,successThreshold);
}
@ -222,8 +243,8 @@ class GuardLine {
_gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount){
var firstOffender = -1;
for(var i = rowStart; i < rowEnd; i+=4){
let firstOffender = -1;
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
// columns to sample
@ -246,36 +267,36 @@ class GuardLine {
return offenderCount;
}
_gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount){
var firstOffender = -1;
for(var i = rowStart; i < rowEnd; i+=4){
// _gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount){
// let firstOffender = -1;
// 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
// columns to sample
if(image[i] > this.blackbarThreshold || image[i+1] > this.blackbarThreshold || image[i+2] > this.blackbarThreshold){
this.conf.debugCanvas.trace(i, DebugCanvasClasses.VIOLATION);
if(firstOffender < 0){
firstOffender = (i - rowStart) >> 2;
offenderCount++;
offenders.push({x: firstOffender, width: 1});
}
else{
offenders[offenderCount].width++
}
}
else{
this.conf.debugCanvas.trace(i, DebugCanvasClasses.GUARDLINE_BLACKBAR);
// is that a black pixel again? Let's reset the 'first offender'
firstOffender = -1;
}
// // we track sections that go over what's supposed to be a black line, so we can suggest more
// // columns to sample
// if(image[i] > this.blackbarThreshold || image[i+1] > this.blackbarThreshold || image[i+2] > this.blackbarThreshold){
// this.aard.debugCanvas.trace(i, DebugCanvasClasses.VIOLATION);
// if(firstOffender < 0){
// firstOffender = (i - rowStart) >> 2;
// offenderCount++;
// offenders.push({x: firstOffender, width: 1});
// }
// else{
// offenders[offenderCount].width++
// }
// }
// else{
// this.aard.debugCanvas.trace(i, DebugCanvasClasses.GUARDLINE_BLACKBAR);
// // is that a black pixel again? Let's reset the 'first offender'
// firstOffender = -1;
// }
}
// }
return offenderCount;
}
// return offenderCount;
// }
_ti_checkRow(image, rowStart, rowEnd, successThreshold) {
for(var i = rowStart; i < rowEnd; i+=4){
_ti_checkRow(image, rowStart, rowEnd, successThreshold): boolean {
for(let i = rowStart; i < rowEnd; i+=4){
if(image[i] > this.imageThreshold || image[i+1] > this.imageThreshold || image[i+2] > this.imageThreshold){
if(successThreshold --<= 0){
return true;
@ -286,20 +307,20 @@ class GuardLine {
return false;
}
_ti_debugCheckRow(image, rowStart, rowEnd, successThreshold) {
for(var i = rowStart; i < rowEnd; i+=4){
if(image[i] > this.imageThreshold || image[i+1] > this.imageThreshold || image[i+2] > this.imageThreshold){
this.conf.debugCanvas.trace(i, DebugCanvasClasses.GUARDLINE_IMAGE);
if(successThreshold --<= 0){
return true;
}
} else {
this.conf.debugCanvas.trace(i, DebugCanvasClasses.WARN);
}
}
// _ti_debugCheckRow(image, rowStart, rowEnd, successThreshold) {
// for(let i = rowStart; i < rowEnd; i+=4){
// if(image[i] > this.imageThreshold || image[i+1] > this.imageThreshold || image[i+2] > this.imageThreshold){
// this.aard.debugCanvas.trace(i, DebugCanvasClasses.GUARDLINE_IMAGE);
// if(successThreshold --<= 0){
// return true;
// }
// } else {
// this.aard.debugCanvas.trace(i, DebugCanvasClasses.WARN);
// }
// }
return false;
}
// return false;
// }
}
export default GuardLine;

View File

@ -2,13 +2,28 @@ import Debug from '../../../conf/Debug';
import EdgeStatus from './enums/EdgeStatusEnum';
import EdgeDetectQuality from './enums/EdgeDetectQualityEnum';
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) {
console.log("Loading EdgeDetect.js");
}
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){
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;
if (direction == EdgeDetectPrimaryDirection.VERTICAL) {
if (direction == EdgeDetectPrimaryDirection.Vertical) {
try {
fastCandidates = this.findCandidates(image, sampleCols, guardLineOut);
if (! this.isValidSample(fastCandidates)) {
return {status: EdgeStatus.AR_UNKNOWN};
return {status: EdgeStatus.ARUnknown};
}
// if(quality == EdgeDetectQuality.FAST){
// edges = fastCandidates; // todo: processing
// } else {
edgeCandidates = this.edgeDetect(image, fastCandidates);
bars = this.edgePostprocess(edgeCandidates, this.conf.canvas.height);
bars = this.edgePostprocess(edgeCandidates);
// }
} catch (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 {
bars = this.pillarTest(image) ? {status: EdgeStatus.AR_KNOWN} : {status: EdgeStatus.AR_UNKNOWN};
bars = this.pillarTest(image) ? {status: EdgeStatus.ARKnown} : {status: EdgeStatus.ARUnknown};
}
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);
var upper_top_corrected = upper_top * this.conf.canvasImageDataRowLength;
var upper_bottom_corrected = upper_bottom * this.conf.canvasImageDataRowLength;
var lower_top_corrected = lower_top * this.conf.canvasImageDataRowLength;
var lower_bottom_corrected = lower_bottom * this.conf.canvasImageDataRowLength;
let upper_top_corrected = upper_top * this.conf.canvasImageDataRowLength;
let upper_bottom_corrected = upper_bottom * this.conf.canvasImageDataRowLength;
let lower_top_corrected = lower_top * this.conf.canvasImageDataRowLength;
let lower_bottom_corrected = lower_bottom * this.conf.canvasImageDataRowLength;
// if(Debug.debugCanvas.enabled){
// this._columnTest_dbgc(image, upper_top_corrected, upper_bottom_corrected, cols_a, res_top, false);
@ -221,20 +236,20 @@ class EdgeDetect{
}
edgeDetect(image, samples){
var edgeCandidatesTop = {count: 0};
var edgeCandidatesBottom = {count: 0};
let edgeCandidatesTop = {count: 0};
let edgeCandidatesBottom = {count: 0};
var detections;
var canvasWidth = this.conf.canvas.width;
var canvasHeight = this.conf.canvas.height;
let detections;
let canvasWidth = this.conf.canvas.width;
let canvasHeight = this.conf.canvas.height;
var sampleStart, sampleEnd, loopEnd;
var sampleRow_black, sampleRow_color;
let sampleStart, sampleEnd, loopEnd;
let sampleRow_black, sampleRow_color;
var blackEdgeViolation = false;
let blackEdgeViolation = false;
var topEdgeCount = 0;
var bottomEdgeCount = 0;
let topEdgeCount = 0;
let bottomEdgeCount = 0;
try {
for (const sample of samples.res_top){
@ -259,7 +274,7 @@ class EdgeDetect{
loopEnd = sampleRow_black + sampleEnd;
if (Debug.debugCanvas.enabled){
blackEdgeViolation = this._blackbarTest_dbg(image, sampleRow_black + sampleStart, loopEnd);
// blackEdgeViolation = this._blackbarTest_dbg(image, sampleRow_black + sampleStart, loopEnd);
} else {
blackEdgeViolation = this._blackbarTest(image, sampleRow_black + sampleStart, loopEnd);
}
@ -274,7 +289,7 @@ class EdgeDetect{
loopEnd = sampleRow_color + sampleEnd;
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 {
this._imageTest(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesTop);
}
@ -302,7 +317,7 @@ class EdgeDetect{
loopEnd = sampleRow_black + sampleEnd;
if(Debug.debugCanvas.enabled){
blackEdgeViolation = this._blackbarTest_dbg(image, sampleRow_black + sampleStart, loopEnd);
// blackEdgeViolation = this._blackbarTest_dbg(image, sampleRow_black + sampleStart, loopEnd);
} else {
blackEdgeViolation = this._blackbarTest(image, sampleRow_black + sampleStart, loopEnd);
}
@ -317,7 +332,7 @@ class EdgeDetect{
loopEnd = sampleRow_color + sampleEnd;
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 {
this._imageTest(image, sampleRow_color + sampleStart, loopEnd, sample.black, edgeCandidatesBottom);
}
@ -335,11 +350,11 @@ class EdgeDetect{
}
edgePostprocess(edges){
var edgesTop = [];
var edgesBottom = [];
var alignMargin = this.conf.canvas.height * this.settings.active.arDetect.allowedMisaligned;
let edgesTop = [];
let edgesBottom = [];
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
// convert objects to array
@ -348,8 +363,8 @@ class EdgeDetect{
delete(edges.edgeCandidatesBottom.count);
if( edges.edgeCandidatesTopCount > 0){
for(var e in edges.edgeCandidatesTop){
var edge = edges.edgeCandidatesTop[e];
for(let e in edges.edgeCandidatesTop){
let edge = edges.edgeCandidatesTop[e];
edgesTop.push({
distance: edge.offset,
absolute: edge.offset,
@ -359,8 +374,8 @@ class EdgeDetect{
}
if( edges.edgeCandidatesBottomCount > 0){
for(var e in edges.edgeCandidatesBottom){
var edge = edges.edgeCandidatesBottom[e];
for(let e in edges.edgeCandidatesBottom){
let edge = edges.edgeCandidatesBottom[e];
edgesBottom.push({
distance: this.conf.canvas.height - edge.offset,
absolute: edge.offset,
@ -388,13 +403,13 @@ class EdgeDetect{
if( 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;
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) ){
return {
status: EdgeStatus.AR_KNOWN,
status: EdgeStatus.ARKnown,
blackbarWidth: blackbarWidth,
guardLineTop: edgesTop[0].distance,
guardLineBottom: edgesBottom[0].absolute,
@ -415,20 +430,20 @@ class EdgeDetect{
// manj vzorcev kot navaden rob.
if (edgesTop[0].length > 1){
var lowMargin = edgesBottom[0].distance - alignMargin;
var highMargin = edgesBottom[0].distance + alignMargin;
let lowMargin = 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){
// dobili smo dejanski rob. vrnimo ga
// 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;
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) ) {
return {
status: EdgeStatus.AR_KNOWN,
status: EdgeStatus.ARKnown,
blackbarWidth: blackbarWidth,
guardLineTop: edgesTop[i].distance,
guardLineBottom: edgesBottom[0].absolute,
@ -446,20 +461,20 @@ class EdgeDetect{
edgesBottom[0].count < this.conf.sampleCols.length * this.settings.active.arDetect.edgeDetection.logoThreshold){
if(edgesBottom[0].length > 1){
var lowMargin = edgesTop[0].distance - alignMargin;
var highMargin = edgesTop[0].distance + alignMargin;
let lowMargin = 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) {
// dobili smo dejanski rob. vrnimo ga
// 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;
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)) {
return {
status: EdgeStatus.AR_KNOWN,
status: EdgeStatus.ARKnown,
blackbarWidth: blackbarWidth,
guardLineTop: edgesTop[0].distance,
guardLineBottom: edgesBottom[i].absolute,
@ -482,10 +497,10 @@ class EdgeDetect{
const edgeDetectionThreshold = this.settings.active.arDetect.edgeDetection.singleSideConfirmationThreshold;
if (edges.edgeCandidatesTopCount == 0 && edges.edgeCandidatesBottomCount != 0){
for(var edge of edgesBottom){
for(let edge of edgesBottom){
if(edge.count >= edgeDetectionThreshold)
return {
status: EdgeStatus.AR_KNOWN,
status: EdgeStatus.ARKnown,
blackbarWidth: edge.distance,
guardLineTop: null,
guardLineBottom: edge.bottom,
@ -496,10 +511,10 @@ class EdgeDetect{
}
}
if (edges.edgeCandidatesTopCount != 0 && edges.edgeCandidatesBottomCount == 0){
for(var edge of edgesTop){
for(let edge of edgesTop){
if(edge.count >= edgeDetectionThreshold)
return {
status: EdgeStatus.AR_KNOWN,
status: EdgeStatus.ARKnown,
blackbarWidth: edge.distance,
guardLineTop: edge.top,
guardLineBottom: null,
@ -512,7 +527,7 @@ class EdgeDetect{
}
// č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.
return {status: EdgeStatus.AR_UNKNOWN}
return {status: EdgeStatus.ARUnknown}
}
pillarTest(image){
@ -523,22 +538,22 @@ class EdgeDetect{
// roughly centered, we return true. Otherwise we return false.
// 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;
var middleRowStart = (this.conf.canvas.height >> 1) * this.conf.canvas.width;
var middleRowEnd = middleRowStart + this.conf.canvas.width - 1;
let middleRowStart = (this.conf.canvas.height >> 1) * this.conf.canvas.width;
let middleRowEnd = middleRowStart + this.conf.canvas.width - 1;
var rowStart = middleRowStart << 2;
var midpoint = (middleRowStart + (this.conf.canvas.width >> 1)) << 2
var rowEnd = middleRowEnd << 2;
let rowStart = middleRowStart << 2;
let midpoint = (middleRowStart + (this.conf.canvas.width >> 1)) << 2
let rowEnd = middleRowEnd << 2;
var edge_left = -1, edge_right = -1;
let edge_left = -1, edge_right = -1;
// preverimo na levi strani
// 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){
edge_left = (i - rowStart) >> 2;
break;
@ -547,7 +562,7 @@ class EdgeDetect{
// preverimo na desni strani
// 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){
edge_right = this.conf.canvas.width - ((i - rowStart) >> 2);
break;
@ -566,9 +581,9 @@ class EdgeDetect{
return false;
}
var edgeError = this.settings.active.arDetect.pillarTest.allowMisaligned;
var error_low = 1 - edgeError;
var error_hi = 1 + edgeError;
let edgeError = this.settings.active.arDetect.pillarTest.allowMisaligned;
let error_low = 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
// 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 lastTmpI = 0;
let edgeDetectCount = 0;
for(const c in colsOut) {
for(const c of colsOut) {
c.diffs = [];
}
if (reverseSearchDirection) {
if (this.settings.active.arDetect.blackbar.antiGradientMode === AntiGradientMode.Disabled) {
// 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++){
if (colsIn[c].blackFound && colsIn[c].imageFound) {
// če smo našli obe točki, potem ne pregledujemo več.
@ -908,7 +923,7 @@ class EdgeDetect{
}
} else {
// 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++){
if (colsIn[c].blackFound && colsIn[c].imageFound) {
// če smo našli obe točki, potem ne pregledujemo več.
@ -995,7 +1010,7 @@ class EdgeDetect{
}
}
} 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++){
if (colsIn[c].blackFound && colsIn[c].imageFound) {
// če smo našli obe točki, potem ne pregledujemo več.
@ -1051,18 +1066,18 @@ class EdgeDetect{
}
_columnTest(image, top, bottom, colsIn, colsOut, reverseSearchDirection){
var tmpI;
_columnTest(image, top: number, bottom: number, colsIn, colsOut, reverseSearchDirection){
let tmpI;
if(reverseSearchDirection){
for(var i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){
for(var col of colsIn){
for(let i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){
for(const col of colsIn){
tmpI = i + (col << 2);
if( image[tmpI] > this.blackbarThreshold ||
image[tmpI + 1] > this.blackbarThreshold ||
image[tmpI + 2] > this.blackbarThreshold ){
var bottom = (i / this.conf.canvasImageDataRowLength) + 1;
const bottom = (i / this.conf.canvasImageDataRowLength) + 1;
colsOut.push({
col: col,
bottom: bottom
@ -1074,8 +1089,8 @@ class EdgeDetect{
break;
}
} else {
for(var i = top; i < bottom; i+= this.conf.canvasImageDataRowLength){
for(var col of colsIn){
for(let i = top; i < bottom; i+= this.conf.canvasImageDataRowLength){
for(const col of colsIn){
tmpI = i + (col << 2);
if( image[tmpI] > this.blackbarThreshold ||
@ -1095,65 +1110,65 @@ class EdgeDetect{
}
}
_columnTest_dbgc(image, top, bottom, colsIn, colsOut, reverseSearchDirection){
var tmpI;
if(reverseSearchDirection){
for(var i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){
for(var col of colsIn){
tmpI = i + (col << 2);
// _columnTest_dbgc(image, top, bottom, colsIn, colsOut, reverseSearchDirection){
// let tmpI;
// if(reverseSearchDirection){
// for(let i = bottom - this.conf.canvasImageDataRowLength; i >= top; i-= this.conf.canvasImageDataRowLength){
// for(let col of colsIn){
// tmpI = i + (col << 2);
if( image[tmpI] > this.blackbarThreshold ||
image[tmpI + 1] > this.blackbarThreshold ||
image[tmpI + 2] > this.blackbarThreshold ){
// if( image[tmpI] > this.blackbarThreshold ||
// image[tmpI + 1] > this.blackbarThreshold ||
// image[tmpI + 2] > this.blackbarThreshold ){
var bottom = (i / this.conf.canvasImageDataRowLength) + 1;
colsOut.push({
col: col,
bottom: bottom
});
colsIn.splice(colsIn.indexOf(col), 1);
this.conf.debugCanvas.trace(tmpI,DebugCanvasClasses.EDGEDETECT_CANDIDATE);
}
else{
this.conf.debugCanvas.trace(tmpI, DebugCanvasClasses.EDGEDETECT_ONBLACK);
}
}
if(colsIn.length < this.colsThreshold)
break;
}
} else {
for(var i = top; i < bottom; i+= this.conf.canvasImageDataRowLength){
for(var col of colsIn){
tmpI = i + (col << 2);
// let bottom = (i / this.conf.canvasImageDataRowLength) + 1;
// colsOut.push({
// col: col,
// bottom: bottom
// });
// colsIn.splice(colsIn.indexOf(col), 1);
// this.conf.debugCanvas.trace(tmpI,DebugCanvasClasses.EDGEDETECT_CANDIDATE);
// }
// else{
// this.conf.debugCanvas.trace(tmpI, DebugCanvasClasses.EDGEDETECT_ONBLACK);
// }
// }
// if(colsIn.length < this.colsThreshold)
// break;
// }
// } else {
// for(let i = top; i < bottom; i+= this.conf.canvasImageDataRowLength){
// for(let col of colsIn){
// tmpI = i + (col << 2);
if( image[tmpI] > this.blackbarThreshold ||
image[tmpI + 1] > this.blackbarThreshold ||
image[tmpI + 2] > this.blackbarThreshold ){
// if( image[tmpI] > this.blackbarThreshold ||
// image[tmpI + 1] > this.blackbarThreshold ||
// image[tmpI + 2] > this.blackbarThreshold ){
colsOut.push({
col: col,
top: (i / this.conf.canvasImageDataRowLength) - 1
});
colsIn.splice(colsIn.indexOf(col), 1);
this.conf.debugCanvas.trace(tmpI, DebugCanvasClasses.EDGEDETECT_CANDIDATE);
if(tmpI-1 > 0){
this.conf.debugCanvas.trace(tmpI - 1, DebugCanvasClasses.EDGEDETECT_CANDIDATE_SECONDARY);
}
if(tmpI+1 < image.length){
this.conf.debugCanvas.trace(tmpI + 1, DebugCanvasClasses.EDGEDETECT_CANDIDATE_SECONDARY);
}
} else {
this.conf.debugCanvas.trace(tmpI, DebugCanvasClasses.EDGEDETECT_ONBLACK);
}
}
if(colsIn.length < this.colsThreshold)
break;
}
}
}
// colsOut.push({
// col: col,
// top: (i / this.conf.canvasImageDataRowLength) - 1
// });
// colsIn.splice(colsIn.indexOf(col), 1);
// this.conf.debugCanvas.trace(tmpI, DebugCanvasClasses.EDGEDETECT_CANDIDATE);
// if(tmpI-1 > 0){
// this.conf.debugCanvas.trace(tmpI - 1, DebugCanvasClasses.EDGEDETECT_CANDIDATE_SECONDARY);
// }
// if(tmpI+1 < image.length){
// this.conf.debugCanvas.trace(tmpI + 1, DebugCanvasClasses.EDGEDETECT_CANDIDATE_SECONDARY);
// }
// } else {
// this.conf.debugCanvas.trace(tmpI, DebugCanvasClasses.EDGEDETECT_ONBLACK);
// }
// }
// if(colsIn.length < this.colsThreshold)
// break;
// }
// }
// }
_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 ||
image[i+1] > this.blackbarThreshold ||
image[i+2] > this.blackbarThreshold ){
@ -1163,25 +1178,25 @@ class EdgeDetect{
return false; // no violation
}
_blackbarTest_dbg(image, start, end){
for(var i = start; i < end; i += 4){
if( image[i ] > this.blackbarThreshold ||
image[i+1] > this.blackbarThreshold ||
image[i+2] > this.blackbarThreshold ){
// _blackbarTest_dbg(image, start, end){
// for(let i = start; i < end; i += 4){
// if( image[i ] > this.blackbarThreshold ||
// image[i+1] > this.blackbarThreshold ||
// image[i+2] > this.blackbarThreshold ){
this.conf.debugCanvas.trace(i, DebugCanvasClasses.VIOLATION)
return true;
} else {
this.conf.debugCanvas.trace(i, DebugCanvasClasses.EDGEDETECT_BLACKBAR)
}
}
return false; // no violation
}
// this.conf.debugCanvas.trace(i, DebugCanvasClasses.VIOLATION)
// return true;
// } else {
// this.conf.debugCanvas.trace(i, DebugCanvasClasses.EDGEDETECT_BLACKBAR)
// }
// }
// return false; // no violation
// }
_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 ||
image[i+1] > this.blackbarThreshold ||
image[i+2] > this.blackbarThreshold ){
@ -1198,28 +1213,28 @@ class EdgeDetect{
}
}
_imageTest_dbg(image, start, end, sampleOffset, edgeCandidates){
var detections = 0;
// _imageTest_dbg(image, start, end, sampleOffset, edgeCandidates){
// let detections = 0;
for(var i = start; i < end; i += 4){
if( image[i ] > this.blackbarThreshold ||
image[i+1] > this.blackbarThreshold ||
image[i+2] > this.blackbarThreshold ){
++detections;
this.conf.debugCanvas.trace(i, DebugCanvasClasses.EDGEDETECT_IMAGE);
} else {
this.conf.debugCanvas.trace(i, DebugCanvasClasses.WARN);
}
}
if(detections >= this.detectionThreshold){
if(edgeCandidates[sampleOffset] != undefined)
edgeCandidates[sampleOffset].count++;
else{
edgeCandidates[sampleOffset] = {offset: sampleOffset, count: 1};
edgeCandidates.count++;
}
}
}
// for(let i = start; i < end; i += 4){
// if( image[i ] > this.blackbarThreshold ||
// image[i+1] > this.blackbarThreshold ||
// image[i+2] > this.blackbarThreshold ){
// ++detections;
// this.conf.debugCanvas.trace(i, DebugCanvasClasses.EDGEDETECT_IMAGE);
// } else {
// this.conf.debugCanvas.trace(i, DebugCanvasClasses.WARN);
// }
// }
// if(detections >= this.detectionThreshold){
// if(edgeCandidates[sampleOffset] != undefined)
// edgeCandidates[sampleOffset].count++;
// else{
// edgeCandidates[sampleOffset] = {offset: sampleOffset, count: 1};
// edgeCandidates.count++;
// }
// }
// }
}

View File

@ -1,6 +0,0 @@
var EdgeDetectPrimaryDirection = Object.freeze({
VERTICAL: 0,
HORIZONTAL: 1
});
export default EdgeDetectPrimaryDirection;

View File

@ -0,0 +1,6 @@
enum EdgeDetectPrimaryDirection {
Vertical = 0,
Horizontal = 1
};
export default EdgeDetectPrimaryDirection;

View File

@ -1,6 +0,0 @@
var EdgeDetectQuality = Object.freeze({
FAST: 0,
IMPROVED: 1
});
export default EdgeDetectQuality;

View File

@ -0,0 +1,6 @@
enum EdgeDetectQuality {
Fast = 0,
Improved = 1
};
export default EdgeDetectQuality;

View File

@ -1,6 +0,0 @@
var EdgeStatus = Object.freeze({
AR_UNKNOWN: 0,
AR_KNOWN: 1,
});
export default EdgeStatus;

View File

@ -0,0 +1,6 @@
enum EdgeStatus {
ARUnknown = 0,
ARKnown = 1,
};
export default EdgeStatus;

View File

@ -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;

View 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;

View File

@ -1,19 +1,33 @@
import Debug from '../../conf/Debug';
import BrowserDetect from '../../conf/BrowserDetect';
import Logger from '../Logger';
import { browser } from 'webextension-polyfill-ts';
import Settings from '../Settings';
if (process.env.CHANNEL !== 'stable'){
console.info("Loading CommsClient");
}
class CommsClient {
commsId: string;
logger: Logger;
settings: any; // sus?
commands: {[x: string]: any[]};
_listener: (m: any) => void;
port: any;
constructor(name, logger, commands) {
try {
this.logger = logger;
if (BrowserDetect.firefox) {
this.port = browser.runtime.connect({name: name});
} else if (BrowserDetect.anyChromium) {
this.port = chrome.runtime.connect({name: name});
}
this.port = browser.runtime.connect(null, {name: name});
this.logger.onLogEnd(
(history) => {
@ -32,6 +46,9 @@ class CommsClient {
this.commsId = (Math.random() * 20).toFixed(0);
this.commands = commands;
} catch (e) {
console.error("CONSTRUCOTR FAILED:", e)
}
}
destroy() {
@ -60,34 +77,11 @@ class CommsClient {
async sendMessage_nonpersistent(message){
message = JSON.parse(JSON.stringify(message)); // vue quirk. We should really use vue store instead
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;
});
}
return browser.runtime.sendMessage(null, message, null);
}
// TODO: sus function — does it get any use?
async requestSettings(){
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);
}
this.settings.active = JSON.parse(response.extensionConf);
this.settings = {active: JSON.parse(response.extensionConf)};
return Promise.resolve(true);
}
async sendMessage(message) {
await this.sendMessage_nonpersistent(message);
return this.sendMessage_nonpersistent(message);
}
registerVideo(){

View File

@ -1,21 +1,34 @@
import Debug from '../../conf/Debug';
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 {
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) {
this.server = server;
this.logger = server.logger;
this.settings = server.settings;
this.ports = [];
this.popupPort = null;
if (BrowserDetect.firefox) {
browser.runtime.onConnect.addListener(p => this.onConnect(p));
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));
}
browser.runtime.onConnect.addListener(p => this.onConnect(p));
browser.runtime.onMessage.addListener((m, sender) => this.processReceivedMessage_nonpersistent(m, sender));
// commands — functions that handle incoming messages
// functions can have the following arguments, which are,
@ -51,14 +64,6 @@ class CommsServer {
'popup-set-selected-tab': [
(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': [
(message, port) => this.server.registerVideo(port.sender),
],
@ -74,28 +79,31 @@ class CommsServer {
'replace-css': [
(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': [
(message, sender, sendResponse) => {
if (BrowserDetect.firefox) {
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);
Promise.resolve(ret);
} else {
sendResponse({extensionConf: JSON.stringify(this.settings.active)});
return true;
}
(message, sender) => {
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);
Promise.resolve(ret);
}
],
'autoar-enable': [
() => {
this.settings.active.sites['@global'].autoar = "blacklist";
this.settings.active.sites['@global'].autoar = ExtensionMode.Enabled;
this.settings.save();
this.logger.log('info', 'comms', "[uw-bg] autoar set to enabled (blacklist). evidenz:", this.settings.active);
}
],
'autoar-disable': [
(message) => {
this.settings.active.sites['@global'].autoar = "disabled";
this.settings.active.sites['@global'].autoar = ExtensionMode.Disabled;
if (message.reason){
this.settings.active.arDetect.disabledReason = message.reason;
} else {
@ -111,7 +119,7 @@ class CommsServer {
// set fairly liberal limit
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();
}
],
@ -140,7 +148,7 @@ class CommsServer {
}
async getCurrentTabHostname() {
const activeTab = await this._getActiveTab();
const activeTab = await this.activeTab;
if (!activeTab || activeTab.length < 1) {
this.logger.log('warn', 'comms', 'There is no active tab for some reason. activeTab:', activeTab);
@ -173,34 +181,28 @@ class CommsServer {
}
}
async _getActiveTab() {
if (BrowserDetect.firefox) {
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;
});
});
}
get activeTab() {
return browser.tabs.query({currentWindow: true, active: 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
async sendToFrameContentScripts(message, tab, frame, port) {
/**
* Sends a message to addon content scripts.
* @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) {
// note: 'port' is _not_ shadowed here.
this.ports[tab][frame][port].postMessage(message);
return;
}
for (const port in this.ports[tab][frame]) {
// note: 'port' is shadowed here!
this.ports[tab][frame][port].postMessage(message);
for (const framePort in this.ports[tab][frame]) {
this.ports[tab][frame][framePort].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);
if (isNaN(tab)) {
@ -233,7 +235,7 @@ class CommsServer {
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);
const tabs = await this._getActiveTab();
const tabs = await this.activeTab;
this.logger.log('info', 'comms', "[CommsServer::_sendToActive] currently active tab(s)?", tabs);
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(
'info', 'comms', '[CommsServer.js::execCmd] Received message', message,
". Port/sender:", portOrSender, "sendResponse:", sendResponse, "\nThere is ", this.commands[message.cmd]?.length ?? 0,
@ -289,7 +294,7 @@ class CommsServer {
if (this.commands[message.cmd]) {
for (const c of this.commands[message.cmd]) {
try {
await c(message, portOrSender, sendResponse);
await c(message, portOrSender);
} catch (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) {
await this.execCmd(message, portOrSender, sendResponse);
async handleMessage(message, portOrSender) {
await this.execCmd(message, portOrSender);
if (message.forwardToSameFramePort) {
this.sendToFrameContentScripts(message, portOrSender.tab.id, portOrSender.frameId, message.port)
@ -323,10 +328,10 @@ class CommsServer {
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.handleMessage(message, sender, sendResponse);
this.handleMessage(message, sender);
}
// chrome shitiness mitigation

View File

@ -1,27 +1,55 @@
import Debug from '../../conf/Debug';
import VideoData from './VideoData';
import RescanReason from './enums/RescanReason';
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
import CropModePersistence from '../../../common/enums/crop-mode-persistence.enum';
import RescanReason from './enums/RescanReason.enum';
import AspectRatioType from '../../../common/enums/AspectRatioType.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'){
console.info("Loading 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){
this.logger = logger;
this.hasVideos = false;
this.siteDisabled = false;
this.videos = [];
this.settings = settings;
this.actionHandlerInitQueue = [];
this.lastUrl = window.location.href;
this.extensionMode = extensionMode;
this.readOnly = readOnly;
this.defaultCrop = undefined;
this.currentCrop = undefined;
if (comms){
this.comms = comms;
@ -54,8 +82,6 @@ class PageInfo {
this.rescan(RescanReason.PERIODIC);
this.scheduleUrlCheck();
this.currentZoomScale = 1;
}
async injectCss(cssString) {
@ -85,9 +111,9 @@ class PageInfo {
if(this.rescanTimer){
clearTimeout(this.rescanTimer);
}
for (var video of this.videos) {
for (let video of this.videos) {
try {
this.comms.unregisterVideo(video.id)
(this.comms.unregisterVideo as any)(video.vdid)
video.destroy();
} catch (e) {
this.logger.log('error', ['debug', 'init'], '[PageInfo::destroy] unable to destroy video! Error:', e);
@ -95,7 +121,7 @@ class PageInfo {
}
try {
playerStyleString = this.settings.active.sites[window.location.hostname].css;
const playerStyleString = this.settings.active.sites[window.location.hostname].css;
if (playerStyleString) {
this.comms.sendMessage({
cmd: 'eject-css',
@ -108,7 +134,7 @@ class PageInfo {
}
reset() {
for(var video of this.videos) {
for(let video of this.videos) {
video.destroy();
}
this.rescan(RescanReason.MANUAL);
@ -124,7 +150,7 @@ class PageInfo {
setActionHandler(actionHandler) {
this.actionHandler = actionHandler;
for (var item of this.actionHandlerInitQueue) {
for (let item of this.actionHandlerInitQueue) {
this.actionHandler.registerHandleMouse(item);
}
this.actionHandlerInitQueue = [];
@ -132,8 +158,8 @@ class PageInfo {
getVideos(host) {
if (this.settings.active.sites[host]?.DOM?.video?.manual
&& this.settings.active.sites[host]?.DOM?.video?.querySelector){
const videos = document.querySelectorAll(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.querySelectors);
if (videos.length) {
return videos;
@ -150,7 +176,7 @@ class PageInfo {
const oldVideoCount = this.videos.length;
try{
var vids = this.getVideos(window.location.hostname);
let vids = this.getVideos(window.location.hostname);
if(!vids || vids.length == 0){
this.hasVideos = false;
@ -164,8 +190,8 @@ class PageInfo {
// add new videos
this.hasVideos = false;
var videoExists = false;
var video, v;
let videoExists = false;
let video, v;
for (video of vids) {
// č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) {
this.comms.registerVideo({host: window.location.hostname, location: window.location});
// this.comms.registerVideo({host: window.location.hostname, location: window.location});
this.comms.registerVideo();
} 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);
}
var ths = this;
let ths = this;
this.rescanTimer = setTimeout(function(rescanReason){
ths.rescanTimer = null;
@ -304,7 +332,7 @@ class PageInfo {
clearTimeout(this.urlCheckTimer);
}
var ths = this;
let ths = this;
this.urlCheckTimer = setTimeout(function(){
ths.urlCheckTimer = null;
@ -329,14 +357,14 @@ class PageInfo {
initArDetection(playingOnly){
if (playingOnly) {
for(var vd of this.videos){
for(let vd of this.videos){
if(vd.isPlaying()) {
vd.initArDetection();
}
}
return;
} else {
for(var vd of this.videos){
for(let vd of this.videos){
vd.initArDetection();
}
}
@ -347,13 +375,13 @@ class PageInfo {
// these need to be called on tab switch
pauseProcessing(playingOnly){
if (playingOnly) {
for(var vd of this.videos){
for(let vd of this.videos){
if (vd.isPlaying()) {
vd.pause();
}
}
} else {
for(var vd of this.videos){
for(let vd of this.videos){
vd.pause();
}
}
@ -361,7 +389,7 @@ class PageInfo {
resumeProcessing(resumeAutoar = false, playingOnly = false){
if (playingOnly) {
for(var vd of this.videos){
for(let vd of this.videos){
if (vd.isPlaying()) {
vd.resume();
if(resumeAutoar){
@ -370,7 +398,7 @@ class PageInfo {
}
}
} else {
for(var vd of this.videos){
for(let vd of this.videos){
vd.resume();
if(resumeAutoar){
vd.resumeAutoAr();
@ -385,13 +413,13 @@ class PageInfo {
this.logger.log('info', 'debug', '[PageInfo::startArDetection()] starting automatic ar detection!')
}
if (playingOnly) {
for(var vd of this.videos){
if (video.isPlaying()) {
for(let vd of this.videos){
if (vd.isPlaying()) {
vd.startArDetection();
}
}
} else {
for(var vd of this.videos){
for(let vd of this.videos){
vd.startArDetection();
}
}
@ -399,28 +427,28 @@ class PageInfo {
stopArDetection(playingOnly){
if (playingOnly) {
for(var vd of this.videos){
for(let vd of this.videos){
if (vd.isPlaying()) {
vd.stopArDetection();
}
}
} else {
for(var vd of this.videos){
for(let vd of this.videos){
vd.stopArDetection();
}
}
}
setAr(ar, playingOnly){
setAr(ar, playingOnly?: boolean){
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);
} else {
this.logger.log('info', 'debug', '[PageInfo::setAr] aspect ratio is auto');
try {
for (var vd of this.videos) {
for (let vd of this.videos) {
if (!playingOnly || vd.isPlaying()) {
vd.resetLastAr();
}
@ -434,14 +462,14 @@ class PageInfo {
}
// TODO: find a way to only change aspect ratio for one video
if (ar === AspectRatio.Reset) {
for (var vd of this.videos) {
if (ar === AspectRatioType.Reset) {
for (let vd of this.videos) {
if (!playingOnly || vd.isPlaying()) {
vd.resetAr();
}
}
} else {
for (var vd of this.videos) {
for (let vd of this.videos) {
if (!playingOnly || vd.isPlaying()) {
vd.setAr(ar)
}
@ -451,78 +479,78 @@ class PageInfo {
setVideoAlignment(videoAlignment, playingOnly) {
if (playingOnly) {
for(var vd of this.videos) {
for(let vd of this.videos) {
if (vd.isPlaying()) {
vd.setVideoAlignment(videoAlignment)
}
}
} else {
for(var vd of this.videos) {
for(let vd of this.videos) {
vd.setVideoAlignment(videoAlignment)
}
}
}
setPanMode(mode, playingOnly) {
setPanMode(mode, playingOnly?: boolean) {
if (playingOnly) {
for(var vd of this.videos) {
for(let vd of this.videos) {
if (vd.isPlaying()) {
vd.setPanMode(mode);
}
}
} else {
for(var vd of this.videos) {
for(let vd of this.videos) {
vd.setPanMode(mode);
}
}
}
restoreAr(playingOnly) {
restoreAr(playingOnly?: boolean) {
if (playingOnly) {
for(var vd of this.videos){
for(let vd of this.videos){
if (vd.isPlaying()) {
vd.restoreAr();
}
}
} else {
for(var vd of this.videos){
for(let vd of this.videos){
vd.restoreAr();
}
}
}
setStretchMode(stretchMode, playingOnly, fixedStretchRatio){
setStretchMode(stretchMode, playingOnly?: boolean, fixedStretchRatio?: boolean){
// TODO: find a way to only change aspect ratio for one video
if (playingOnly) {
for(var vd of this.videos){
for(let vd of this.videos){
if (vd.isPlaying()) {
vd.setStretchMode(stretchMode, fixedStretchRatio)
}
}
} else {
for(var vd of this.videos){
for(let vd of this.videos){
vd.setStretchMode(stretchMode, fixedStretchRatio)
}
}
}
setZoom(zoomLevel, no_announce, playingOnly) {
setZoom(zoomLevel, no_announce?: boolean, playingOnly?: boolean) {
if (playingOnly) {
for(var vd of this.videos) {
for(let vd of this.videos) {
if (vd.isPlaying()) {
vd.setZoom(zoomLevel, no_announce);
}
}
} else {
for(var vd of this.videos) {
for(let vd of this.videos) {
vd.setZoom(zoomLevel, no_announce);
}
}
}
zoomStep(step, playingOnly) {
for(var vd of this.videos){
zoomStep(step, playingOnly?: boolean) {
for(let vd of this.videos){
if (!playingOnly || vd.isPlaying()) {
vd.zoomStep(step);
}
@ -530,19 +558,19 @@ class PageInfo {
}
markPlayer(name, color) {
for (var vd of this.videos) {
for (let vd of this.videos) {
vd.markPlayer(name,color);
}
}
unmarkPlayer() {
for (var vd of this.videos) {
for (let vd of this.videos) {
vd.unmarkPlayer();
}
}
announceZoom(scale) {
if (this.announceZoomTimeout) {
clearTimeout(this.announceZoom);
clearTimeout(this.announceZoomTimeout);
}
this.currentZoomScale = scale;
const ths = this;
@ -550,13 +578,13 @@ class PageInfo {
}
setManualTick(manualTick) {
for(var vd of this.videos) {
vd.setManualTick();
for(let vd of this.videos) {
vd.setManualTick(manualTick);
}
}
tick() {
for(var vd of this.videos) {
for(let vd of this.videos) {
vd.tick();
}
}

View File

@ -1,11 +1,14 @@
import Debug from '../../conf/Debug';
import ExtensionMode from '../../../common/enums/extension-mode.enum'
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
import ExtensionMode from '../../../common/enums/ExtensionMode.enum'
import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
import PlayerNotificationUi from '../uwui/PlayerNotificationUI';
import PlayerUi from '../uwui/PlayerUI';
import BrowserDetect from '../../conf/BrowserDetect';
import _ from 'lodash';
import * as _ from 'lodash';
import { sleep } from '../../../common/js/utils';
import VideoData from './VideoData';
import Settings from '../Settings';
import Logger from '../Logger';
if (process.env.CHANNEL !== 'stable'){
console.info("Loading: PlayerData.js");
@ -40,6 +43,31 @@ if (process.env.CHANNEL !== 'stable'){
class PlayerData {
//#region helper objects
logger: Logger;
videoData: VideoData;
settings: Settings;
notificationService: PlayerNotificationUi;
//#endregion
//#region HTML objects
video: any;
element: any;
overlayNode: any;
//#endregion
//#region flags
invalid: boolean = false;
private periodicallyRefreshPlayerElement: boolean = false;
halted: boolean = true;
//#region misc stuff
extensionMode: any;
dimensions: any;
private playerIdElement: any;
private observer: MutationObserver;
//#endregion
constructor(videoData) {
try {
this.logger = videoData.logger;
@ -84,7 +112,7 @@ class PlayerData {
}
onPlayerDimensionsChanged(mutationList, observer) {
onPlayerDimensionsChanged(mutationList?, observer?) {
if (this?.checkPlayerSizeChange()) {
this.videoData.resizer.restore();
}
@ -179,7 +207,7 @@ class PlayerData {
this.destroyOverlay();
}
var overlay = document.createElement('div');
let overlay = document.createElement('div');
overlay.style.width = '100%';
overlay.style.height = '100%';
overlay.style.position = 'absolute';

View File

@ -2,13 +2,45 @@ import Debug from '../../conf/Debug';
import PlayerData from './PlayerData';
import Resizer from '../video-transform/Resizer';
import ArDetector from '../ar-detect/ArDetector';
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
import _ from 'lodash';
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 {
//#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){
this.vdid = (Math.random()*100).toFixed();
@ -38,21 +70,18 @@ class VideoData {
async onVideoLoaded() {
if (!this.videoLoaded) {
// 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
/**
* 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
}
// const hasDrmResult = hasDrm(this.video);
// if (hasDrmResult === undefined) {
// console.info('video not playing or doesnt exist. doing nothing.');
// }
this.logger.log('info', 'init', '%c[VideoData::onVideoLoaded] ——————————— Initiating phase two of videoData setup ———————————', 'color: #0f9');
this.videoLoaded = true;
@ -280,7 +309,7 @@ class VideoData {
if (this.pageInfo.defaultCrop) {
this.resizer.setAr(this.pageInfo.defaultCrop);
} else {
this.resizer.reset();
this.resizer.reset();
try {
this.stopArDetection();
@ -299,7 +328,7 @@ class VideoData {
this.validationId = validationId;
while (!this.destroyed && !this.invalid && this.validationId === validationId) {
await this.sleep(500);
await sleep(500);
this.doPeriodicFallbackChangeDetectionCheck();
}
}
@ -308,11 +337,6 @@ class VideoData {
this.validateVideoOffsets();
}
async sleep(timeout) {
return new Promise( (resolve) => setTimeout(() => resolve(), timeout));
}
onVideoDimensionsChanged(mutationList, observer) {
if (!mutationList || this.video === undefined) { // something's wrong
if (observer && this.video) {
@ -404,7 +428,7 @@ class VideoData {
* Gets the contents of the style attribute of the video element
* in a form of an object.
*/
getVideoStyle() {
getVideoStyle(): any {
// This will _always_ give us an array. Empty string gives an array
// that contains one element. That element is an empty string.
const styleArray = (this.video.getAttribute('style') || '').split(';');
@ -463,11 +487,6 @@ class VideoData {
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
return;
}
if (hasDrm(this.video)) {
this.player.showNotification('AARD_DRM');
// return;
}
if (this.arDetector){
this.arDetector.init();
}
@ -483,6 +502,11 @@ class VideoData {
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
return;
}
if (hasDrm(this.video)) {
this.player.showNotification('AARD_DRM');
}
if (!this.arDetector) {
this.initArDetection();
}
@ -520,7 +544,7 @@ class VideoData {
}
this.paused = false;
try {
this.resizer.start();
// this.resizer.start();
if (this.player) {
this.player.start();
}
@ -555,12 +579,12 @@ class VideoData {
this.resizer.setLastAr(lastAr);
}
setAr(ar, lastAr){
setAr(ar, lastAr?){
if (this.invalid) {
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();
}
@ -581,7 +605,7 @@ class VideoData {
this.resizer.setLastAr('original');
}
panHandler(event, forcePan) {
panHandler(event, forcePan?: boolean) {
if (this.invalid) {
return;
}

View File

@ -0,0 +1,7 @@
enum RescanReason {
PERIODIC = 0,
URL_CHANGE = 1,
MANUAL = 2
};
export default RescanReason;

View File

@ -1,7 +0,0 @@
var RescanReason = Object.freeze({
PERIODIC: 0,
URL_CHANGE: 1,
MANUAL: 2
});
export default RescanReason;

View File

@ -24,7 +24,7 @@ class CssHandler {
}
}
for (var i in styleArray) {
for (let 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
@ -45,7 +45,7 @@ class CssHandler {
static buildStyleString(styleArray) {
let styleString = '';
for(var i in styleArray) {
for(let i in styleArray) {
if(styleArray[i]) {
styleString += styleArray[i] + "; ";
}

View File

@ -3,40 +3,65 @@ 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 ExtensionMode from '../../../common/enums/ExtensionMode.enum';
import StretchType from '../../../common/enums/StretchType.enum';
import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum';
import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
import CropModePersistance from '../../../common/enums/CropModePersistence.enum';
import { sleep } from '../Util';
import Logger from '../Logger';
import Settings from '../Settings';
import VideoData from '../video-data/VideoData';
if(Debug.debug) {
console.log("Loading: Resizer.js");
}
class Resizer {
//#region flags
canPan: boolean = false;
destroyed: boolean = false;
//#endregion
//#region helper objects
logger: Logger;
settings: Settings;
scaler: Scaler;
stretcher: Stretcher;
zoom: Zoom;
conf: VideoData;
//#endregion
//#region HTML elements
video: any;
//#endregion
//#region data
correctedVideoDimensions: any;
currentCss: any;
currentStyleString: string;
currentPlayerStyleString: any;
currentCssValidFor: any;
currentVideoSettings: any;
lastAr: {type: any, ratio?: number} = {type: AspectRatioType.Initial};
resizerId: any;
videoAlignment: any;
userCss: string;
userCssClassName: any;
pan: any = null;
//#endregion
constructor(videoData) {
this.resizerId = (Math.random(99)*100).toFixed(0);
this.resizerId = (Math.random()*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;
@ -47,7 +72,6 @@ class Resizer {
this.canPan = false;
}
this.userCss = '';
this.userCssClassName = videoData.userCssClassName;
}
@ -74,13 +98,13 @@ class Resizer {
calculateRatioForLegacyOptions(ar){
// also present as modeToAr in Scaler.js
if (ar.type !== AspectRatio.FitWidth && ar.type !== AspectRatio.FitHeight && ar.ratio) {
if (ar.type !== AspectRatioType.FitWidth && ar.type !== AspectRatioType.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;
// handles "legacy" options, such as 'fit to widht', 'fit to height' and AspectRatioType.Reset. No zoom tho
let ratioOut;
if (!this.conf.video) {
this.logger.log('info', 'debug', "[Scaler.js::modeToAr] No video??",this.conf.video, "killing videoData");
@ -102,15 +126,15 @@ class Resizer {
// 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;
let fileAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
if (ar.type === AspectRatio.FitWidth){
if (ar.type === AspectRatioType.FitWidth){
ar.ratio = ratioOut > fileAr ? ratioOut : fileAr;
}
else if(ar.type === AspectRatio.FitHeight){
else if(ar.type === AspectRatioType.FitHeight){
ar.ratio = ratioOut < fileAr ? ratioOut : fileAr;
}
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);
ar.ratio = fileAr;
} else {
@ -128,7 +152,7 @@ class Resizer {
// 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.type === AspectRatioType.Automatic || ar.type === AspectRatioType.Fixed) {
if (!ar.ratio || isNaN(ar.ratio)) {
return;
}
@ -140,7 +164,7 @@ class Resizer {
}
}
async setAr(ar, lastAr) {
async setAr(ar: {type: any, ratio?: number}, lastAr?: {type: any, ratio?: number}) {
if (this.destroyed) {
return;
}
@ -157,11 +181,12 @@ class Resizer {
}
const siteSettings = this.settings.active.sites[window.location.hostname];
let stretchFactors: {xFactor: number, yFactor: number, arCorrectionFactor?: number, ratio?: number} | any;
// reset zoom, but only on aspect ratio switch. We also know that aspect ratio gets converted to
// AspectRatio.Fixed when zooming, so let's keep that in mind
// AspectRatioType.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 !== AspectRatioType.Fixed && ar.type !== AspectRatioType.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
) {
@ -173,9 +198,9 @@ class Resizer {
// this means here's the optimal place to set or forget aspect ratio. Saving of current crop ratio
// is handled in pageInfo.updateCurrentCrop(), which also makes sure to persist aspect ratio if ar
// is set to persist between videos / through current session / until manual reset.
if (ar.type === AspectRatio.Automatic ||
ar.type === AspectRatio.Reset ||
ar.type === AspectRatio.Initial ) {
if (ar.type === AspectRatioType.Automatic ||
ar.type === AspectRatioType.Reset ||
ar.type === AspectRatioType.Initial ) {
// reset/undo default
this.conf.pageInfo.updateCurrentCrop(undefined);
} else {
@ -196,7 +221,7 @@ class Resizer {
this.lastAr = {type: ar.type, ratio: ar.ratio}
}
// if (this.extensionMode === ExtensionMode.Basic && !PlayerData.isFullScreen() && ar.type !== AspectRatio.Reset) {
// if (this.extensionMode === ExtensionMode.Basic && !PlayerData.isFullScreen() && ar.type !== AspectRatioType.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;
@ -211,20 +236,20 @@ class Resizer {
// * 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) {
if (ar.type !== AspectRatioType.Automatic || this.stretcher.mode === StretchType.Basic) {
this.conf?.arDetector?.pause();
} else {
if (this.lastAr.type === AspectRatio.Automatic) {
if (this.lastAr.type === AspectRatioType.Automatic) {
this.conf?.arDetector?.unpause();
}
}
// do stretch thingy
if (this.stretcher.mode === Stretch.NoStretch
|| this.stretcher.mode === Stretch.Conditional
|| this.stretcher.mode === Stretch.FixedSource){
if (this.stretcher.mode === StretchType.NoStretch
|| this.stretcher.mode === StretchType.Conditional
|| this.stretcher.mode === StretchType.FixedSource){
var stretchFactors = this.scaler.calculateCrop(ar);
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);
@ -243,27 +268,27 @@ class Resizer {
}
}
if (this.stretcher.mode === Stretch.Conditional){
if (this.stretcher.mode === StretchType.Conditional){
this.stretcher.applyConditionalStretch(stretchFactors, ar.ratio);
} else if (this.stretcher.mode === Stretch.FixedSource) {
} else if (this.stretcher.mode === StretchType.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',
this.stretcher.mode === StretchType.NoStretch ? 'stretch-free crop.' :
this.stretcher.mode === StretchType.Conditional ? 'crop with conditional StretchType.' : 'crop with fixed stretch',
'Stretch factors are:', stretchFactors
);
} else if (this.stretcher.mode === Stretch.Hybrid) {
var stretchFactors = this.stretcher.calculateStretch(ar.ratio);
} else if (this.stretcher.mode === StretchType.Hybrid) {
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 if (this.stretcher.mode === StretchType.Fixed) {
stretchFactors = this.stretcher.calculateStretchFixed(ar.ratio)
} else if (this.stretcher.mode === StretchType.Basic) {
stretchFactors = this.stretcher.calculateBasicStretch();
this.logger.log('info', 'debug', '[Resizer::setAr] Processed stretch factors for basic StretchType. Stretch factors are:', stretchFactors);
} else {
var stretchFactors = {xFactor: 1, yFactor: 1};
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);
}
@ -271,7 +296,11 @@ class Resizer {
this.stretcher.chromeBugMitigation(stretchFactors);
var translate = this.computeOffsets(stretchFactors);
let translate = this.computeOffsets(stretchFactors);
console.log("aspect ratio will be set. stretch factors:", stretchFactors, "translate:", translate);
this.applyCss(stretchFactors, translate);
}
@ -279,13 +308,13 @@ class Resizer {
toFixedAr() {
// converting to fixed AR means we also turn off autoAR
this.setAr({
ar: this.lastAr.ar,
type: AspectRatio.Fixed
ratio: this.lastAr.ratio,
type: AspectRatioType.Fixed
});
}
resetLastAr() {
this.lastAr = {type: AspectRatio.Initial};
this.lastAr = {type: AspectRatioType.Initial};
}
setLastAr(override){
@ -296,7 +325,7 @@ class Resizer {
return this.lastAr;
}
setStretchMode(stretchMode, fixedStretchRatio){
setStretchMode(stretchMode, fixedStretchRatio?){
this.stretcher.setStretchMode(stretchMode, fixedStretchRatio);
this.restore();
}
@ -307,10 +336,10 @@ class Resizer {
return;
}
// dont allow weird floats
this.videoAlignment = VideoAlignment.Center;
this.videoAlignment = VideoAlignmentType.Center;
// because non-fixed aspect ratios reset panning:
if (this.lastAr.type !== AspectRatio.Fixed) {
if (this.lastAr.type !== AspectRatioType.Fixed) {
this.toFixedAr();
}
@ -326,7 +355,7 @@ class Resizer {
}
resetPan() {
this.pan = {};
this.pan = {x: 0, y: 0};
this.videoAlignment = this.settings.getDefaultVideoAlignment(window.location.hostname);
}
@ -334,7 +363,7 @@ class Resizer {
// 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 = {};
this.pan = {x: 0, y: 0};
}
if (this.settings.active.miscSettings.mousePanReverseMouse) {
@ -356,8 +385,8 @@ class Resizer {
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});
if(this.lastAr.type === AspectRatioType.Initial){
this.setAr({type: AspectRatioType.Reset});
}
else {
if (this.lastAr?.ratio === null) {
@ -373,7 +402,7 @@ class Resizer {
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});
this.setAr({type: AspectRatioType.Reset});
}
setPanMode(mode) {
@ -386,10 +415,6 @@ class Resizer {
}
}
resetPan(){
this.pan = undefined;
}
setZoom(zoomLevel, no_announce) {
this.zoom.setZoom(zoomLevel, no_announce);
}
@ -404,11 +429,11 @@ class Resizer {
}
resetCrop(){
this.setAr({type: AspectRatio.Reset});
this.setAr({type: AspectRatioType.Reset});
}
resetStretch(){
this.stretcher.setStretchMode(Stretch.NoStretch);
this.stretcher.setStretchMode(StretchType.NoStretch);
this.restore();
}
@ -489,7 +514,7 @@ class Resizer {
const hdiff = this.conf.player.dimensions.height - realVideoHeight;
if (wdiff < 0 && hdiff < 0 && this.zoom.scale > 1) {
this.conf.player.restore();
this.conf.resizer.restore();
}
const wdiffAfterZoom = realVideoWidth * stretchFactors.xFactor - this.conf.player.dimensions.width;
@ -503,22 +528,22 @@ class Resizer {
if (this.pan) {
if (this.pan.relativeOffsetX || this.pan.relativeOffsetY) {
// 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) {
if (this.videoAlignment == VideoAlignmentType.Left) {
translate.x += wdiffAfterZoom * 0.5;
}
else if (this.videoAlignment == VideoAlignment.Right) {
else if (this.videoAlignment == VideoAlignmentType.Right) {
translate.x -= wdiffAfterZoom * 0.5;
}
}
this.logger.log('info', ['debug', 'resizer'], "[Resizer::_res_computeOffsets] <rid:"+this.resizerId+"> calculated offsets:\n\n",
console.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},
@ -578,7 +603,7 @@ class Resizer {
}
}
for (var i in styleArray) {
for (let 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
@ -601,7 +626,7 @@ class Resizer {
buildStyleString(styleArray) {
let styleString = '';
for(var i in styleArray) {
for(let i in styleArray) {
if(styleArray[i]) {
styleString += styleArray[i] + " !important; ";
}

View File

@ -1,6 +1,8 @@
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
@ -8,7 +10,10 @@ import BrowserDetect from '../../conf/BrowserDetect';
class Scaler {
// internal variables
//#region helper objects
conf: VideoData;
logger: Logger;
//#endregion
// functions
constructor(videoData) {
@ -19,13 +24,13 @@ class Scaler {
// 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
// handles "legacy" options, such as 'fit to widht', 'fit to height' and AspectRatioType.Reset. No zoom tho
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;
}
var ratioOut;
let ratioOut;
if (!this.conf.video) {
this.logger.log('error', 'debug', "[Scaler.js::modeToAr] No video??",this.conf.video, "killing videoData");
@ -46,19 +51,19 @@ class Scaler {
// 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;
let fileAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
if (ar.type === AspectRatio.FitWidth) {
if (ar.type === AspectRatioType.FitWidth) {
ratioOut > fileAr ? ratioOut : fileAr
ar.ratio = ratioOut;
return ratioOut;
}
else if (ar.type === AspectRatio.FitHeight) {
else if (ar.type === AspectRatioType.FitHeight) {
ratioOut < fileAr ? ratioOut : fileAr
ar.ratio = 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)
ar.ar = fileAr;
return fileAr;
@ -76,7 +81,7 @@ class Scaler {
* undoes any zoom that style="height:123%" on the video element adds.
*
* 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.
* * When player is wider than stream, we want to undo any height compensations site
* tacks on the video tag.
@ -94,10 +99,10 @@ class Scaler {
let arCorrectionFactor = 1;
if (ar.type !== AspectRatio.FitHeight) {
if (ar.type !== AspectRatioType.FitHeight) {
if (playerAr < compensatedStreamAr) {
arCorrectionFactor = this.conf.player.dimensions.width / this.conf.video.offsetWidth;
} else if (ar.type !== AspectRatio.Reset) {
} else if (ar.type !== AspectRatioType.Reset) {
arCorrectionFactor /= heightCompensationFactor;
}
}
@ -116,7 +121,7 @@ class Scaler {
return {error: "illegal_video_dimensions"};
}
if (ar.type === AspectRatio.Reset){
if (ar.type === AspectRatioType.Reset){
return {xFactor: arCorrectionFactor, yFactor: arCorrectionFactor, arCorrectionFactor: arCorrectionFactor}
}
@ -139,7 +144,7 @@ class Scaler {
// Dejansko razmerje stranic datoteke/<video> značke
// 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;
}

View File

@ -1,6 +1,9 @@
import Stretch from '../../../common/enums/stretch.enum';
import StretchType from '../../../common/enums/StretchType.enum';
import BrowserDetect from '../../conf/BrowserDetect';
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
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)
// transform: scale(x,y) se uporablja za raztegovanje videa, ne pa za približevanje
@ -8,8 +11,20 @@ import AspectRatio from '../../../common/enums/aspect-ratio.enum';
// transform: scale(x,y) is used for stretching, not zooming.
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
constructor(videoData) {
@ -20,11 +35,11 @@ class Stretcher {
this.fixedStretchRatio = undefined;
}
setStretchMode(stretchMode, fixedStretchRatio) {
if (stretchMode === Stretch.Default) {
setStretchMode(stretchMode, fixedStretchRatio?) {
if (stretchMode === StretchType.Default) {
this.mode = this.settings.getDefaultStretchMode(window.location.hostname);
} else {
if (stretchMode === Stretch.Fixed || stretchMode == Stretch.FixedSource) {
if (stretchMode === StretchType.Fixed || stretchMode == StretchType.FixedSource) {
this.fixedStretchRatio = fixedStretchRatio;
}
this.mode = stretchMode;
@ -32,17 +47,17 @@ class Stretcher {
}
applyConditionalStretch(stretchFactors, actualAr){
var playerAr = this.conf.player.dimensions.width / this.conf.player.dimensions.height;
var videoAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
let playerAr = this.conf.player.dimensions.width / this.conf.player.dimensions.height;
let videoAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
if (! actualAr){
actualAr = playerAr;
}
var newWidth = this.conf.video.offsetWidth * stretchFactors.xFactor;
var newHeight = this.conf.video.offsetHeight * stretchFactors.yFactor;
let newWidth = this.conf.video.offsetWidth * stretchFactors.xFactor;
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
if(actualAr < videoAr){
@ -53,11 +68,11 @@ class Stretcher {
actualWidth = newWidth;
}
var 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 minW = 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);
var maxH = this.conf.player.dimensions.height * (1 + this.settings.active.stretch.conditionalDifferencePercent);
let minH = 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) {
stretchFactors.xFactor *= this.conf.player.dimensions.width / actualWidth;
@ -145,7 +160,7 @@ squeezeFactor: ${squeezeFactor}`, '\nvideo', this.conf.video);
return arCorrectionFactor;
}
calculateStretch(actualAr, playerArOverride) {
calculateStretch(actualAr, playerArOverride?) {
const playerAr = playerArOverride || this.conf.player.dimensions.width / this.conf.player.dimensions.height;
const streamAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
@ -153,7 +168,7 @@ squeezeFactor: ${squeezeFactor}`, '\nvideo', this.conf.video);
actualAr = playerAr;
}
var stretchFactors = {
let stretchFactors: any = {
xFactor: 1,
yFactor: 1
};
@ -245,7 +260,7 @@ squeezeFactor: ${squeezeFactor}`, '\nvideo', this.conf.video);
* * 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 Stretch.Basic no matter what you put in `transform: scale(x,y)`.
* 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).

View File

@ -1,19 +1,32 @@
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
// calculates zooming and video offsets/panning
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) {
this.conf = videoData;
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(){
@ -41,7 +54,7 @@ class Zoom {
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, "!");
// NOTE: SCALE IS NOT LOGARITHMIC

View File

@ -1,18 +1,9 @@
import Debug from './conf/Debug.js';
import BrowserDetect from './conf/BrowserDetect';
import CommsServer from './lib/comms/CommsServer';
import Settings from './lib/Settings';
import Logger from './lib/Logger';
/**
* NOTE: we cannot get rid of this js file. I tried for 30 seconds and I couldn't get
* extension to work unless I kept this part of extension out of the ts file.
*/
import { sleep } from '../common/js/utils';
// 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';
import UWServer from './UWServer';
var BgVars = {
arIsActive: true,
@ -20,316 +11,7 @@ var BgVars = {
currentSite: ""
}
class 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();
const server = new UWServer();
window.sendUnmarkPlayer = (message) => {
server.sendUnmarkPlayer(message)

View File

@ -1,13 +1,10 @@
import Debug from './conf/Debug';
import BrowserDetect from './conf/BrowserDetect';
import ExtensionMode from '../common/enums/extension-mode.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 from './lib/Logger';
/**
* NOTE: we cannot get rid of this js file. I tried for 30 seconds and I couldn't get
* extension to work unless I kept this part of extension out of the ts file.
*/
import UWContent from './UWContent';
import BrowserDetect from './conf/BrowserDetect';
if(process.env.CHANNEL !== 'stable'){
console.warn("\n\n\n\n\n\n ——— Sᴛλʀᴛɪɴɢ Uʟᴛʀᴀɪɪʏ ———\n << ʟᴏᴀᴅɪɴɢ ᴍᴀɪɴ ꜰɪʟᴇ >>\n\n\n\n");
@ -27,161 +24,5 @@ if (BrowserDetect.edge) {
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
}
class UW {
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)],
}
}
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) {
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();
const main = new UWContent();
main.init();

View File

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

View File

@ -111,11 +111,11 @@
<script>
import Donate from '../common/misc/Donate.vue';
import SuperAdvancedSettings from './SuperAdvancedSettings.vue';
import Debug from '../ext/conf/Debug.js';
import BrowserDetect from '../ext/conf/BrowserDetect.js';
import ExtensionConf from '../ext/conf/ExtensionConf.js';
import ObjectCopy from '../ext/lib/ObjectCopy.js';
import Settings from '../ext/lib/Settings.js';
import Debug from '../ext/conf/Debug';
import BrowserDetect from '../ext/conf/BrowserDetect';
import ExtensionConf from '../ext/conf/ExtensionConf';
import ObjectCopy from '../ext/lib/ObjectCopy';
import Settings from '../ext/lib/Settings';
import GeneralSettings from './GeneralSettings';
import ControlsSettings from './controls-settings/ControlsSettings';
import AddEditActionPopup from './controls-settings/AddEditActionPopup';

View File

@ -57,16 +57,16 @@
</div>
<div class="flex flex-row button-box">
<Button label="Left"
:selected="settings.active.sites['@global'].videoAlignment === VideoAlignment.Left"
@click.native="setDefaultvideoAlignment(VideoAlignment.Left)">
:selected="settings.active.sites['@global'].videoAlignment === VideoAlignmentType.Left"
@click.native="setDefaultvideoAlignment(VideoAlignmentType.Left)">
</Button>
<Button label="Center"
:selected="settings.active.sites['@global'].videoAlignment === VideoAlignment.Center"
@click.native="setDefaultvideoAlignment(VideoAlignment.Center)">
:selected="settings.active.sites['@global'].videoAlignment === VideoAlignmentType.Center"
@click.native="setDefaultvideoAlignment(VideoAlignmentType.Center)">
</Button>
<Button label="Right"
:selected="settings.active.sites['@global'].videoAlignment === VideoAlignment.Right"
@click.native="setDefaultvideoAlignment(VideoAlignment.Right)">
:selected="settings.active.sites['@global'].videoAlignment === VideoAlignmentType.Right"
@click.native="setDefaultvideoAlignment(VideoAlignmentType.Right)">
</Button>
</div>
@ -75,20 +75,20 @@
</div>
<div class="flex flex-row button-box">
<Button label="Don't stretch"
:selected="settings.active.sites['@global'].stretch === Stretch.NoStretch"
@click.native="setDefaultStretchingMode(Stretch.NoStretch)">
:selected="settings.active.sites['@global'].stretch === StretchType.NoStretch"
@click.native="setDefaultStretchingMode(StretchType.NoStretch)">
</Button>
<Button label="Basic stretch"
:selected="settings.active.sites['@global'].stretch === Stretch.Basic"
@click.native="setDefaultStretchingMode(Stretch.Basic)">
:selected="settings.active.sites['@global'].stretch === StretchType.Basic"
@click.native="setDefaultStretchingMode(StretchType.Basic)">
</Button>
<Button label="Hybrid stretch"
:selected="settings.active.sites['@global'].stretch === Stretch.Hybrid"
@click.native="setDefaultStretchingMode(Stretch.Hybrid)">
:selected="settings.active.sites['@global'].stretch === StretchType.Hybrid"
@click.native="setDefaultStretchingMode(StretchType.Hybrid)">
</Button>
<Button label="Thin borders only"
:selected="settings.active.sites['@global'].stretch === Stretch.Conditional"
@click.native="setDefaultStretchingMode(Stretch.Conditional)"
:selected="settings.active.sites['@global'].stretch === StretchType.Conditional"
@click.native="setDefaultStretchingMode(StretchType.Conditional)"
>
</Button>
</div>
@ -108,7 +108,7 @@
<div class="flex flex-input">
<input type="number"
step="any"
:value="settings.active.stretch.conditionalDifferencePercent"
:value="settings.active.StretchType.conditionalDifferencePercent"
@input="updateStretchThreshold($event.target.value)"
>
</div>
@ -152,9 +152,9 @@
<script>
import Button from '../common/components/Button';
import Stretch from '../common/enums/stretch.enum';
import ExtensionMode from '../common/enums/extension-mode.enum';
import VideoAlignment from '../common/enums/video-alignment.enum';
import StretchType from '../common/enums/StretchType.enum';
import ExtensionMode from '../common/enums/ExtensionMode.enum';
import VideoAlignmentType from '../common/enums/VideoAlignmentType.enum';
import BrowserDetect from '../ext/conf/BrowserDetect';
export default {
@ -198,7 +198,7 @@ export default {
if (!newThreshold || isNaN(newThreshold)) {
return;
}
this.settings.active.stretch.conditionalDifferencePercent = newThreshold;
this.settings.active.StretchType.conditionalDifferencePercent = newThreshold;
this.settings.save();
},
resetSettings() {

View File

@ -102,7 +102,7 @@
<script>
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 CommandChain from './command-builder/CommandChain';
import CommandAddEdit from './command-builder/CommandAddEdit';

View File

@ -111,7 +111,7 @@
<script>
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';
export default {

View File

@ -89,7 +89,7 @@
<script>
import ActionList from '../../../ext/conf/ActionList';
import Stretch from '../../../common/enums/stretch.enum';
import StretchType from '../../../common/enums/StretchType.enum';
export default {
data () {

View File

@ -219,7 +219,7 @@ import Settings from '../ext/lib/Settings';
import ExecAction from './js/ExecAction.js';
import DefaultSettingsPanel from './panels/DefaultSettingsPanel';
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 {ChromeShittinessMitigations as CSM} from '../common/js/ChromeShittinessMitigations';

View File

@ -59,7 +59,7 @@ class ExecAction {
this.settings.active.sites[site].stretch = cmd.arg;
} else if (cmd.action === "set-alignment") {
this.settings.active.sites[site].videoAlignment = cmd.arg;
} else if (cmd.action === "set-extension-mode") {
} else if (cmd.action === "set-ExtensionMode") {
this.settings.active.sites[site].mode = cmd.arg;
} else if (cmd.action === "set-autoar-mode") {
this.settings.active.sites[site].autoar = cmd.arg;

View File

@ -130,9 +130,9 @@
import ShortcutButton from '../../common/components/ShortcutButton.vue';
import QsElement from '../../common/components/QsElement.vue';
import QuerySelectorSetting from '../../common/components/QuerySelectorSetting.vue';
import ExtensionMode from '../../common/enums/extension-mode.enum';
import VideoAlignment from '../../common/enums/video-alignment.enum';
import Stretch from '../../common/enums/stretch.enum';
import ExtensionMode from '../../common/enums/ExtensionMode.enum';
import VideoAlignmentType from '../../common/enums/VideoAlignmentType.enum';
import StretchType from '../../common/enums/StretchType.enum';
export default {
components: {
QuerySelectorSetting,
@ -186,8 +186,8 @@ export default {
mode: ExtensionMode.Default,
autoar: ExtensionMode.Default,
type: 'user-added',
stretch: Stretch.Default,
videoAlignment: VideoAlignment.Default,
stretch: StretchType.Default,
videoAlignment: VideoAlignmentType.Default,
keyboardShortcutsEnabled: ExtensionMode.Default,
}
}

View File

@ -125,7 +125,7 @@ import ExecAction from '../js/ExecAction';
import KeyboardShortcutParser from '../../common/js/KeyboardShortcutParser';
import ShortcutButton from '../../common/components/ShortcutButton';
import ComputeActionsMixin from '../../common/mixins/ComputeActionsMixin';
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
import CropModePersistence from '../../common/enums/CropModePersistence.enum';
export default {
data() {

16
tsconfig.json Normal file
View 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/**/*" ],
}

View File

@ -22,19 +22,23 @@ const config = {
path: __dirname + `/dist-${process.env.BROWSER == 'firefox' ? 'ff' : process.env.BROWSER}`,
filename: '[name].js',
},
devtool: "source-map",
resolve: {
// maybe we'll move to TS some day, but today is not the day
extensions: [
// '.ts', '.tsx',
'.ts', '.tsx',
'.js', '.vue'
],
},
module: {
rules: [
// {
// test: /\.tsx?$/,
// loader: 'ts-loader',
// },
{
test: /\.ts$/,
loader: 'ts-loader',
exclude: /node_modules/
},
{
test: /\.vue$/,
loaders: 'vue-loader',