Compare commits

...

5 Commits

35 changed files with 907 additions and 3648 deletions

View File

@ -54,12 +54,20 @@ export type SettingsReloadFlags = true | SettingsReloadComponent;
export interface AardSettings { export interface AardSettings {
aardType: 'webgl' | 'legacy' | 'auto'; aardType: 'webgl' | 'legacy' | 'auto';
earlyStopOptions: {
stopAfterFirstDetection: boolean;
stopAfterTimeout: boolean;
stopTimeout: number;
},
disabledReason: string, // if automatic aspect ratio has been disabled, show reason disabledReason: string, // if automatic aspect ratio has been disabled, show reason
allowedMisaligned: number, // top and bottom letterbox thickness can differ by this much. allowedMisaligned: number, // top and bottom letterbox thickness can differ by this much.
// Any more and we don't adjust ar. // Any more and we don't adjust ar.
allowedArVariance: number, // amount by which old ar can differ from the new (1 = 100%) allowedArVariance: number, // amount by which old ar can differ from the new (1 = 100%)
timers: { // autodetection frequency timers: { // autodetection frequency
playing: number, // while playing playing: number, // while playing
playingReduced: number, // while video/player element has insufficient size
paused: number, // while paused paused: number, // while paused
error: number, // after error error: number, // after error
minimumTimeout: number, minimumTimeout: number,
@ -86,51 +94,6 @@ export interface AardSettings {
}, },
}, },
// NOTE: Black Frame is currently not in use.
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.
},
// Used by old aspect ratio detection algorithm. Pls remove.
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
},
// Also not in use, probs.
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
},
blackLevels: { blackLevels: {
defaultBlack: number, // By default, pixels darker than this are considered black. defaultBlack: number, // By default, pixels darker than this are considered black.
// (If detection algorithm detects darker blacks, black is considered darkest detected pixel) // (If detection algorithm detects darker blacks, black is considered darkest detected pixel)
@ -144,20 +107,6 @@ export interface AardSettings {
randomCols: number, // we add this many randomly selected columns to the static columns 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, 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
},
arSwitchLimiter: { // to be implemented
switches: number, // we can switch this many times
period: number // per this period
},
// pls deprecate and move things used // pls deprecate and move things used
edgeDetection: { edgeDetection: {
@ -173,7 +122,7 @@ export interface AardSettings {
minQualitySecondEdge: number, // The other edge must reach this quality (must be smaller or equal to single edge quality) minQualitySecondEdge: number, // The other edge must reach this quality (must be smaller or equal to single edge quality)
} }
maxLetterboxOffset: 0.1, // Upper and lower letterbox can be different by this many (% of height) maxLetterboxOffset: number, // Upper and lower letterbox can be different by this many (% of height)
// Previous iteration variables VVVV // Previous iteration variables VVVV
sampleWidth: number, // we take a sample this wide for edge detection sampleWidth: number, // we take a sample this wide for edge detection
@ -190,6 +139,7 @@ export interface AardSettings {
minColsForSearch: number, // if we hit the edge of blackbars for all but this many columns (%-wise), we don't 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 // 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) // are now. (NOTE: keep this less than 1 in case we implement logo detection)
edgeMismatchTolerancePx: number,// corners and center are considered equal if they differ by at most this many px
}, },
pillarTest: { pillarTest: {
ignoreThinPillarsPx: number, // ignore pillars that are less than this many pixels thick. ignoreThinPillarsPx: number, // ignore pillars that are less than this many pixels thick.

View File

@ -321,7 +321,11 @@ export default {
this.handleMessage(event); this.handleMessage(event);
}); });
this.eventBus.subscribe('uw-config-broadcast', {function: (data) => { this.eventBus.subscribeMulti(
{
'uw-config-broadcast': {
function:
(data) => {
switch (data.type) { switch (data.type) {
case 'drm-status': case 'drm-status':
this.statusFlags.hasDrm = data.hasDrm; this.statusFlags.hasDrm = data.hasDrm;
@ -333,9 +337,10 @@ export default {
this.playerDimensionsUpdate(data.data); this.playerDimensionsUpdate(data.data);
break; break;
} }
}}); }
},
this.eventBus.subscribe('uw-set-ui-state', { function: (data) => { 'uw-set-ui-state': {
function: (data) => {
if (data.globalUiVisible !== undefined) { if (data.globalUiVisible !== undefined) {
if (this.isGlobal) { if (this.isGlobal) {
if (data.globalUiVisible) { if (data.globalUiVisible) {
@ -357,11 +362,9 @@ export default {
this.hideUwWindow(true); this.hideUwWindow(true);
} }
} }
}}); }
},
this.eventBus.subscribe( 'uw-restore-ui-state': {
'uw-restore-ui-state',
{
function: (data) => { function: (data) => {
if (this.saveState) { if (this.saveState) {
if (this.saveState.uwWindowVisible) { if (this.saveState.uwWindowVisible) {
@ -372,44 +375,53 @@ export default {
} }
this.saveState = {}; this.saveState = {};
} }
},
'uw-restore-ui-state': {
function: (data) => {
if (this.saveState) {
if (this.saveState.uwWindowVisible) {
this.showUwWindow();
} }
); this.uwWindowFadeOutDisabled = this.saveState.uwWindowFadeOutDisabled;
this.uwWindowFadeOut = this.saveState.uwWindowFadeOut;
this.eventBus.subscribe('ui-trigger-zone-update', { }
this.saveState = {};
}
},
'ui-trigger-zone-update': {
function: (data) => { function: (data) => {
this.showTriggerZonePreview = data.previewZoneVisible; this.showTriggerZonePreview = data.previewZoneVisible;
// this.; // this.;
} }
}); },
'start-trigger-zone-edit': {
this.eventBus.subscribe(
'start-trigger-zone-edit',
{
function: () => { function: () => {
this.triggerZoneEditorVisible = true; this.triggerZoneEditorVisible = true;
this.uwWindowVisible = false; this.uwWindowVisible = false;
} }
} },
); 'finish-trigger-zone-edit': {
this.eventBus.subscribe(
'finish-trigger-zone-edit',
{
function: () => { function: () => {
this.triggerZoneEditorVisible = false; this.triggerZoneEditorVisible = false;
this.showUwWindow('playerUiSettings'); this.showUwWindow('playerUiSettings');
} }
} },
},
this
); );
this.sendToParentLowLevel('uwui-get-role', null); this.sendToParentLowLevel('uwui-get-role', null);
this.sendToParentLowLevel('uwui-get-theme', null); this.sendToParentLowLevel('uwui-get-theme', null);
// console.log('player overlay created get player dims:')
this.sendToParentLowLevel('uw-bus-tunnel', { this.sendToParentLowLevel('uw-bus-tunnel', {
action: 'get-player-dimensions' action: 'get-player-dimensions'
}); });
}, },
destroyed() {
this.eventBus.unsubscribeAll(this)
},
methods: { methods: {
/** /**
* Gets URL of the browser settings page (i think?) * Gets URL of the browser settings page (i think?)

View File

@ -148,6 +148,7 @@ export default {
this.eventBus.subscribe( this.eventBus.subscribe(
'set-current-site', 'set-current-site',
{ {
source: this,
function: (config, context) => { function: (config, context) => {
if (this.site) { if (this.site) {
if (!this.site.host) { if (!this.site.host) {
@ -169,7 +170,7 @@ export default {
this.loadFrames(this.site); this.loadFrames(this.site);
} }
} },
); );
this.comms = new CommsClient('popup-port', this.logger, this.eventBus); this.comms = new CommsClient('popup-port', this.logger, this.eventBus);

View File

@ -204,13 +204,19 @@ export default {
this.eventBus.subscribe( this.eventBus.subscribe(
'uw-show-ui', 'uw-show-ui',
() => { {
source: this,
function: () => {
if (this.inPlayer) { if (this.inPlayer) {
return; // show-ui is only intended for global overlay return; // show-ui is only intended for global overlay
} }
},
} }
) )
}, },
destroyed() {
this.eventBus.unsubscribeAll(this);
},
methods: { methods: {
/** /**
* Gets URL of the browser settings page (i think?) * Gets URL of the browser settings page (i think?)

View File

@ -3,11 +3,14 @@
<div class="flex flex-row flex-wrap"> <div class="flex flex-row flex-wrap">
<!-- AARD performance metrics --> <!-- AARD performance metrics -->
<div class="sub-panel"> <div>
<div class="flex flex-row"> <div class="flex flex-row">
<h1><mdicon name="television-play" :size="32" /> Automatic Aspect Ratio Detection</h1> <h1>Automatic Aspect Ratio Detection</h1>
</div> </div>
<div class="sub-panel-content"> <div class="aard-settings-group">
<!-- the last time i tried to comment out this block, it didn't work properly v-if="false" it is -->
<div v-if="false">
<p> <p>
<b>Autodetection performance</b> <b>Autodetection performance</b>
</p> </p>
@ -186,39 +189,66 @@
</div> </div>
</div> </div>
<div class="settings-segment"> </div>
<h2>Basic settings</h2>
<div class="option">
<div class="name"> <div class="settings-segment">
Autodetection frequency <!-- <h2>Basic settings</h2> -->
<!-- <div class="field">
<div class="label">
Stop autodetection after first detection:
</div> </div>
<div class="description"> <div class="">
Shorter intervals (left side of the slider) are more responsive to changes in aspect ratio detections, <input type="checkbox" v-model="settings.active.arDetect.earlyStopOptions.stopAfterFirstDetection" />
but requires more system resources.
</div> </div>
<div class="indent"> </div>
<div class="flex flex-row row-padding"> <div class="field">
<div class="flex flex-input"> <div class="label">
More often&nbsp;<small>(~60/s)</small> Stop detection after a period of time:
<input type="range" </div>
<div class="">
<input type="checkbox" v-model="settings.active.arDetect.earlyStopOptions.stopAfterTimeout" />
</div>
</div>
<div class="field">
<div class="label">
Stop detection after:
</div>
<div class="input">
<input type="input" v-model="settings.active.arDetect.earlyStopOptions.stopTimeout" />
<div class="unit">seconds</div>
</div>
</div> -->
<div class="field">
<div class="label">Autodetection frequency (time between samples)</div>
<div class="range-input">
<input
type="range"
:value="Math.log(settings.active.arDetect.timers.playing)" :value="Math.log(settings.active.arDetect.timers.playing)"
@change="setArCheckFrequency($event.target.value)" @change="setArCheckFrequency($event.target.value)"
min="2.3" min="2.3"
max="9.3" max="9.3"
step="any" step="0.01"
/> />
&nbsp; Less often&nbsp;<small>(~1/10s)</small> <input
</div> v-model="settings.active.arDetect.timers.playing"
</div> class="input"
type="text"
>
<div class="unit">ms</div>
</div> </div>
</div> </div>
<div class="option"> <div class="field">
<div class="name"> <div class="label">Frame extraction canvas type:</div>
Autodetection sensitivity <div class="select">
</div> <select v-model="settings.active.arDetect.aardType">
<div class="description"> <option value="auto">Automatic</option>
<option value="webgl">WebGL only</option>
<option value="fallback">Legacy / fallback</option>
</select>
</div> </div>
</div> </div>
@ -227,31 +257,14 @@
<div class="input"> <div class="input">
<input v-model="settings.active.arDetect.allowedMisaligned" /> <input v-model="settings.active.arDetect.allowedMisaligned" />
</div> </div>
<div class="description"> <div class="hint">
Ultrawidify detects letterbox only if video is vertically centered. Some people are bad at vertically Ultrawidify detects letterbox only if video is vertically centered. Some people are bad at vertically
centering the content, though. This is how off-center the video can be before autodetection will centering the content, though. This is how off-center the video can be before autodetection will
refuse to crop it. refuse to crop it (% of total height).
</div> </div>
</div> </div>
<div class="option">
<div class="name">Video sample size</div>
<div class="input">
<input v-model="settings.active.arDetect.canvasDimensions.sampleCanvas.width" /> x <input v-model="settings.active.arDetect.canvasDimensions.sampleCanvas.height" />
</div> </div>
</div> </div>
<div class="field">
<div class="label">Sample columns:</div>
<div class="input"><input v-model="settings.active.arDetect.sampling.staticCols" /></div>
</div>
<div class="field">
<div class="label">Sample rows:</div>
<div class="input"><input v-model="settings.active.arDetect.sampling.staticRows" /></div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -285,7 +298,16 @@ export default {
'site' 'site'
], ],
created() { created() {
this.eventBus.subscribe('uw-config-broadcast', {function: (config) => this.handleConfigBroadcast(config)}); this.eventBus.subscribe(
'uw-config-broadcast',
{
source: this,
function: (config) => this.handleConfigBroadcast(config)
}
);
},
destroyed() {
this.eventBus.unsubscribeAll(this);
}, },
mounted() { mounted() {
this.eventBus.sendToTunnel('get-aard-timing'); this.eventBus.sendToTunnel('get-aard-timing');
@ -306,6 +328,9 @@ export default {
async openOptionsPage() { async openOptionsPage() {
BrowserDetect.runtime.openOptionsPage(); BrowserDetect.runtime.openOptionsPage();
}, },
setArCheckFrequency(event) {
this.settings.active.arDetect.timers.playing = Math.floor(Math.pow(Math.E, event));
},
refreshGraph() { refreshGraph() {
this.eventBus.sendToTunnel('get-aard-timing'); this.eventBus.sendToTunnel('get-aard-timing');
}, },
@ -326,6 +351,10 @@ export default {
<style lang="scss" scoped module> <style lang="scss" scoped module>
@import '../res-common/variables'; @import '../res-common/variables';
// .aard-settings-group {
// max-width: 69rem;
// }
.performance-graph-container { .performance-graph-container {
position: relative; position: relative;

View File

@ -373,11 +373,20 @@ export default({
'isPopup' 'isPopup'
], ],
created() { created() {
this.eventBus.subscribe('uw-config-broadcast', {function: (config) => this.handleElementStack(config)}); this.eventBus.subscribe(
'uw-config-broadcast',
{
source: this,
function: (config) => this.handleElementStack(config)
}
);
}, },
mounted() { mounted() {
this.getPlayerTree(); this.getPlayerTree();
}, },
destroyed() {
this.eventBus.unsubscribeAll(this);
},
computed: {}, computed: {},
methods: { methods: {
getPlayerTree() { getPlayerTree() {

View File

@ -241,24 +241,7 @@ export default {
max-width: 24rem; max-width: 24rem;
} }
.range-input {
display: flex;
flex-direction: row;
* {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
input {
max-width: 5rem;
}
input[type=range] {
max-width: none;
}
}
.trigger-zone-editor { .trigger-zone-editor {
background-color: rgba(0,0,0,0.25); background-color: rgba(0,0,0,0.25);

View File

@ -66,49 +66,6 @@
></StretchOptionsPanel> ></StretchOptionsPanel>
</div> </div>
</div> </div>
<div class="flex flex-col">
<!-- VIDEO ALIGNMENT -->
<div class="sub-panel">
<div class="flex flex-row">
<mdicon name="align-horizontal-center" :size="32" />
<h1>Video alignment:</h1>
</div>
<div class="flex flex-row justify-center mt-4">
<alignment-options-control-component
:eventBus="eventBus"
>
</alignment-options-control-component>
</div>
<!-- <div class="flex flex-row flex-wrap">
<div class="m-t-0-33em display-block">
<input id="_input_zoom_site_allow_pan"
type="checkbox"
/>
Pan with mouse
</div>
</div> -->
</div>
<!-- ZOOM OPTIONS -->
<!-- <div class="sub-panel">
<div class="flex flex-row">
<mdicon name="magnify-plus-outline" :size="32" />
<h1>Manual zoom:</h1>
</div>
<ZoomOptionsPanel
:settings="settings"
:siteSettings="siteSettings"
:eventBus="eventBus"
:isEditing="editMode"
></ZoomOptionsPanel>
</div> -->
</div>
</div> </div>
</div> </div>
</template> </template>
@ -153,11 +110,20 @@ export default {
'site' 'site'
], ],
created() { created() {
this.eventBus.subscribe('uw-config-broadcast', {function: (config) => this.handleConfigBroadcast(config)}); this.eventBus.subscribe(
'uw-config-broadcast',
{
source: this,
function: (config) => this.handleConfigBroadcast(config)
}
);
}, },
mounted() { mounted() {
this.eventBus.sendToTunnel('get-ar'); this.eventBus.sendToTunnel('get-ar');
}, },
destroyed() {
this.eventBus.unsubscribeAll(this);
},
components: { components: {
ShortcutButton, ShortcutButton,
EditShortcutButton, EditShortcutButton,

View File

@ -303,8 +303,6 @@ export default {
return value.replaceAll(',', '.').split('.', 2).join('.').replace(/[^0-9.\-]/g, ''); return value.replaceAll(',', '.').split('.', 2).join('.').replace(/[^0-9.\-]/g, '');
}, },
setValue(key, originalValue, isTextInput) { setValue(key, originalValue, isTextInput) {
console.log('trying to set value:', key, value, isTextInput);
let value = originalValue; let value = originalValue;
if (isTextInput) { if (isTextInput) {
value = (+this.forceNumber(value) / 100); value = (+this.forceNumber(value) / 100);
@ -312,8 +310,6 @@ export default {
value = +this.forceNumber(value); value = +this.forceNumber(value);
} }
console.log('rocessed value:', value);
if (isNaN(+value)) { if (isNaN(+value)) {
value = 0.5; value = 0.5;
} }

View File

@ -32,28 +32,6 @@
<h1>Zoom:</h1> <h1>Zoom:</h1>
</div> </div>
<ZoomOptionsPanel
style="margin-top: -2rem"
:settings="settings"
:eventBus="eventBus"
:siteSettings="siteSettings"
:isEditing="false"
>
</ZoomOptionsPanel>
<div class="flex flex-row">
<mdicon name="crop" :size="24" />&nbsp;&nbsp;
<h1>Video alignment:</h1>
</div>
<div class="flex flex-row">
<alignment-options-control-component
:eventBus="eventBus"
>
</alignment-options-control-component>
</div>
</div> </div>
</template> </template>
@ -81,11 +59,20 @@ export default {
CropOptionsPanel, StretchOptionsPanel, ZoomOptionsPanel CropOptionsPanel, StretchOptionsPanel, ZoomOptionsPanel
}, },
created() { created() {
this.eventBus.subscribe('uw-config-broadcast', {function: (config) => this.handleConfigBroadcast(config)}); this.eventBus.subscribe(
'uw-config-broadcast',
{
source: this,
function: (config) => this.handleConfigBroadcast(config)
}
);
}, },
mounted() { mounted() {
this.eventBus.sendToTunnel('get-ar'); this.eventBus.sendToTunnel('get-ar');
}, },
destroyed() {
this.eventBus.unsubscribeAll(this);
},
methods: { methods: {
} }

View File

@ -73,18 +73,25 @@ button, .button {
align-items: center; align-items: center;
&.l2 {
margin-left: 4rem;
}
.label { .label {
flex: 0 0 25%; flex: 0 0 25%;
text-align: right; text-align: right;
padding-right: 1rem; padding-right: 1rem;
} }
.input {
.input, .range-input {
flex: 0 0 70%; flex: 0 0 70%;
max-width: 24rem;
background-color: rgba($blackBg, $normalTransparentOpacity); background-color: rgba($blackBg, $normalTransparentOpacity);
border: 1px solid transparent; border: 1px solid transparent;
border-bottom: 1px solid rgba(255,255,255,0.5); border-bottom: 1px solid rgba(255,255,255,0.5);
padding-top: 0.25rem; padding-top: 0.25rem;
padding-bottom: 0.25rem; padding-bottom: 0.25rem;
position: relative;
&:active, &:focus, &:focus-within { &:active, &:focus, &:focus-within {
border-bottom: 1px solid rgba($primary, 0.5); border-bottom: 1px solid rgba($primary, 0.5);
@ -97,7 +104,37 @@ button, .button {
background-color: transparent; background-color: transparent;
color: #fff; color: #fff;
} }
.unit {
position: absolute;
right: 0px;
pointer-events: none;
opacity: 0.69;
font-size: 0.8rem;
top: 0;
transform: translateY(69%);
} }
}
.range-input {
display: flex;
flex-direction: row;
* {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
input {
max-width: 5rem;
}
input[type=range] {
max-width: none;
}
}
.hint { .hint {
padding-left: calc(25% + 1rem); padding-left: calc(25% + 1rem);
font-size: 0.8rem; font-size: 0.8rem;

View File

@ -77,6 +77,7 @@ export default class UWContent {
this.eventBus.subscribe( this.eventBus.subscribe(
'uw-restart', 'uw-restart',
{ {
source: this,
function: () => this.initPhase2() function: () => this.initPhase2()
} }
); );
@ -121,6 +122,7 @@ export default class UWContent {
} }
destroy() { destroy() {
this.eventBus.unsubscribeAll(this);
if (this.pageInfo) { if (this.pageInfo) {
this.pageInfo.destroy(); this.pageInfo.destroy();
} }

View File

@ -24,27 +24,27 @@ export default class UWServer {
} }
eventBusCommands = { eventBusCommands = {
'popup-set-selected-tab': [{ 'popup-set-selected-tab': {
function: (message) => this.setSelectedTab(message.selectedMenu, message.selectedSubitem) function: (message) => this.setSelectedTab(message.selectedMenu, message.selectedSubitem)
}], },
'has-video': [{ 'has-video': {
function: (message, context) => this.registerVideo(context.comms.sender) function: (message, context) => this.registerVideo(context.comms.sender)
}], },
'noVideo' : [{ 'noVideo' : {
function: (message, context) => this.unregisterVideo(context.comms.sender) function: (message, context) => this.unregisterVideo(context.comms.sender)
}], },
'inject-css': [{ 'inject-css': {
function: (message, context) => this.injectCss(message.cssString, context.comms.sender) function: (message, context) => this.injectCss(message.cssString, context.comms.sender)
}], },
'eject-css': [{ 'eject-css': {
function: (message, context) => this.removeCss(message.cssString, context.comms.sender) function: (message, context) => this.removeCss(message.cssString, context.comms.sender)
}], },
'replace-css': [{ 'replace-css': {
function: (message, context) => this.replaceCss(message.oldCssString, message.newCssString, context.comms.sender) function: (message, context) => this.replaceCss(message.oldCssString, message.newCssString, context.comms.sender)
}], },
'get-current-site': [{ 'get-current-site': {
function: (message, context) => this.getCurrentSite() function: (message, context) => this.getCurrentSite()
}] }
}; };
private gcTimeout: any; private gcTimeout: any;
@ -84,11 +84,8 @@ export default class UWServer {
this.eventBus = new EventBus({isUWServer: true}); this.eventBus = new EventBus({isUWServer: true});
for (const action in this.eventBusCommands) { this.eventBus.subscribeMulti(this.eventBusCommands, this);
for (const command of this.eventBusCommands[action]) {
this.eventBus.subscribe(action, command);
}
}
this.comms = new CommsServer(this); this.comms = new CommsServer(this);
this.eventBus.setComms(this.comms); this.eventBus.setComms(this.comms);

View File

@ -16,12 +16,20 @@ if(Debug.debug)
const ExtensionConf: SettingsInterface = { const ExtensionConf: SettingsInterface = {
arDetect: { arDetect: {
aardType: 'auto', aardType: 'auto',
earlyStopOptions: {
stopAfterFirstDetection: false,
stopAfterTimeout: false,
stopTimeout: 30,
},
disabledReason: "", // if automatic aspect ratio has been disabled, show reason disabledReason: "", // if automatic aspect ratio has been disabled, show reason
allowedMisaligned: 0.05, // top and bottom letterbox thickness can differ by this much. allowedMisaligned: 0.05, // top and bottom letterbox thickness can differ by this much.
// Any more and we don't adjust ar. // Any more and we don't adjust ar.
allowedArVariance: 0.075, // amount by which old ar can differ from the new (1 = 100%) allowedArVariance: 0.0125,// amount by which old ar can differ from the new (1 = 100%)
timers: { // autodetection frequency timers: { // autodetection frequency
playing: 333, // while playing playing: 333, // while playing
playingReduced: 5000, // while playing at small sizes
paused: 3000, // while paused paused: 3000, // while paused
error: 3000, // after error error: 3000, // after error
minimumTimeout: 5, minimumTimeout: 5,
@ -48,48 +56,6 @@ const ExtensionConf: SettingsInterface = {
}, },
}, },
// samplingInterval: 10, // we sample at columns at (width/this) * [ 1 .. this - 1]
blackframe: {
sufficientColorVariance: 0.10, // 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: 1600,
cumulativeThresholdStrict: 2560,// 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: 0.6, // 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: 10, // everything darker than 10/255 across all RGB components is considered black by
// default. blackLevel can decrease if we detect darker black.
threshold: 16, // 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: 4, // threshold, but when doing blackframe test
imageThreshold: 16, // in order to detect pixel as "not black", the pixel must be brighter than
// the sum of black level, threshold and this value.
gradientThreshold: 2, // 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: 16, // How far do we look to find the gradient
maxGradient: 6, // if two neighboring pixels in gradientSampleSize differ by more than this, then we aren't
// looking at a gradient
gradientNegativeTreshold: -2,
gradientMaxSD: 6, // reserved for future use
antiGradientMode: AntiGradientMode.Lax,
},
variableBlackbarThresholdOptions: { // In case of poor bitrate videos, jpeg artifacts may cause us issues
// FOR FUTURE USE
enabled: true, // allow increasing blackbar threshold
disableArDetectOnMax: true, // disable autodetection when threshold goes over max blackbar threshold
maxBlackbarThreshold: 48, // max threshold (don't increase past this)
thresholdStep: 8, // when failing to set aspect ratio, increase threshold by this much
increaseAfterConsecutiveResets: 2 // increase if AR resets this many times in a row
},
blackLevels: { blackLevels: {
defaultBlack: 16, defaultBlack: 16,
blackTolerance: 4, blackTolerance: 4,
@ -101,19 +67,6 @@ const ExtensionConf: SettingsInterface = {
randomCols: 0, // we add this many randomly selected columns to the static columns randomCols: 0, // we add this many randomly selected columns to the static columns
staticRows: 9, // forms grid with staticSampleCols. Determined in the same way. For black frame checks staticRows: 9, // 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: true,
ignoreEdgeMargin: 0.20, // we ignore anything that pokes over the black line this close to the edge
// (relative to width of the sample)
imageTestThreshold: 0.1, // when testing for image, this much pixels must be over blackbarThreshold
edgeTolerancePx: 2, // 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
},
arSwitchLimiter: { // to be implemented
switches: 2, // we can switch this many times
period: 2.0 // per this period
},
edgeDetection: { edgeDetection: {
slopeTestWidth: 8, slopeTestWidth: 8,
gradientTestSamples: 8, gradientTestSamples: 8,
@ -122,7 +75,7 @@ const ExtensionConf: SettingsInterface = {
gradientTestMinDelta: 8, gradientTestMinDelta: 8,
thresholds: { thresholds: {
edgeDetectionLimit: 8, edgeDetectionLimit: 12,
minQualitySingleEdge: 6, minQualitySingleEdge: 6,
minQualitySecondEdge: 3, minQualitySecondEdge: 3,
}, },
@ -143,6 +96,8 @@ const ExtensionConf: SettingsInterface = {
minColsForSearch: 0.5, // if we hit the edge of blackbars for all but this many columns (%-wise), we don't minColsForSearch: 0.5, // 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 // 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) // are now. (NOTE: keep this less than 1 in case we implement logo detection)
edgeMismatchTolerancePx: 3, // corners and center are considered equal if they differ by at most this many px
}, },
pillarTest: { pillarTest: {
ignoreThinPillarsPx: 5, // ignore pillars that are less than this many pixels thick. ignoreThinPillarsPx: 5, // ignore pillars that are less than this many pixels thick.

View File

@ -4,6 +4,7 @@ import CommsServer from './comms/CommsServer';
export interface EventBusCommand { export interface EventBusCommand {
isGlobal?: boolean, isGlobal?: boolean,
source?: any,
function: (commandData: any, context?: any) => void | Promise<void> function: (commandData: any, context?: any) => void | Promise<void>
} }
@ -24,8 +25,6 @@ export interface EventBusContext {
export default class EventBus { export default class EventBus {
private commands: { [x: string]: EventBusCommand[]} = {}; private commands: { [x: string]: EventBusCommand[]} = {};
private downstreamBuses: EventBus[] = [];
private upstreamBus?: EventBus;
private comms?: CommsClient | CommsServer; private comms?: CommsClient | CommsServer;
private disableTunnel: boolean = false; private disableTunnel: boolean = false;
@ -46,9 +45,6 @@ export default class EventBus {
//#region lifecycle //#region lifecycle
destroy() { destroy() {
this.commands = null; this.commands = null;
for (const bus of this.downstreamBuses) {
bus.destroy();
}
this.destroyIframeTunnelling(); this.destroyIframeTunnelling();
} }
//#endregion //#endregion
@ -57,38 +53,6 @@ export default class EventBus {
this.comms = comms; this.comms = comms;
} }
//#region bus hierarchy management (single page)
setUpstreamBus(eventBus: EventBus, stopRecursing: boolean = false) {
this.upstreamBus = eventBus;
if (!stopRecursing) {
this.upstreamBus.addDownstreamBus(this, true);
}
}
unsetUpstreamBus(stopRecursing: boolean = false) {
if (!stopRecursing) {
this.upstreamBus.removeDownstreamBus(this, false);
}
this.upstreamBus = undefined;
}
addDownstreamBus(eventBus: EventBus, stopRecursing: boolean = false) {
if (!this.downstreamBuses.includes(eventBus)) {
this.downstreamBuses.push(eventBus);
if (!stopRecursing) {
eventBus.setUpstreamBus(this, true);
}
}
}
removeDownstreamBus(eventBus: EventBus, stopRecursing: boolean = false) {
this.downstreamBuses = this.downstreamBuses.filter(x => x !== eventBus);
if (!stopRecursing) {
eventBus.unsetUpstreamBus(true);
}
}
subscribe(commandString: string, command: EventBusCommand) { subscribe(commandString: string, command: EventBusCommand) {
if (!this.commands[commandString]) { if (!this.commands[commandString]) {
this.commands[commandString] = [command]; this.commands[commandString] = [command];
@ -97,8 +61,31 @@ export default class EventBus {
} }
} }
subscribeMulti(commands: {[commandString: string]: EventBusCommand}, source?: any) {
for (const key in commands) {
this.subscribe(
key,
{
...commands[key],
source: source ?? commands[key].source
}
);
}
}
/**
* Removes all commands from a given source
* @param source
*/
unsubscribeAll(source: any) {
for (const commandString in this.commands) {
this.commands[commandString] = this.commands[commandString].filter(x => x.source !== source);
}
}
send(command: string, commandData: any, context?: EventBusContext) { send(command: string, commandData: any, context?: EventBusContext) {
// execute commands we have subscriptions for // execute commands we have subscriptions for
if (this.commands?.[command]) { if (this.commands?.[command]) {
for (const eventBusCommand of this.commands[command]) { for (const eventBusCommand of this.commands[command]) {
eventBusCommand.function(commandData, context); eventBusCommand.function(commandData, context);
@ -114,10 +101,6 @@ export default class EventBus {
if (context?.stopPropagation) { if (context?.stopPropagation) {
return; return;
} }
// propagate commands across the bus
this.sendUpstream(command, commandData, context);
this.sendDownstream(command, commandData, context);
} }
//#endregion //#endregion
@ -127,6 +110,7 @@ export default class EventBus {
* @param config * @param config
*/ */
sendToTunnel(command: string, config: any) { sendToTunnel(command: string, config: any) {
console.log('sending to tunnel from eventBus ....', this)
if (!this.disableTunnel) { if (!this.disableTunnel) {
window.parent.postMessage( window.parent.postMessage(
{ {
@ -145,25 +129,6 @@ export default class EventBus {
} }
} }
private sendDownstream(command: string, config: any, context?: EventBusContext, sourceEventBus?: EventBus) {
for (const eventBus of this.downstreamBuses) {
if (eventBus !== sourceEventBus) {
// prevent eventBus.send from auto-propagating the command
eventBus.send(command, config, {...context, stopPropagation: true});
eventBus.sendDownstream(command, config);
}
}
}
private sendUpstream(command: string, config: any, context?: EventBusContext) {
if (this.upstreamBus) {
// prevent eventBus.send from auto-propagating the command
this.upstreamBus.send(command, config, {...context, stopPropagation: true});
this.upstreamBus.sendUpstream(command, config, context);
this.upstreamBus.sendDownstream(command, config, context, this);
}
}
//#region iframe tunnelling //#region iframe tunnelling
private setupIframeTunnelling() { private setupIframeTunnelling() {
// forward messages coming from iframe tunnels // forward messages coming from iframe tunnels

View File

@ -10,7 +10,7 @@ import { GlCanvas } from './gl/GlCanvas';
import { AardCanvasStore } from './interfaces/aard-canvas-store.interface'; import { AardCanvasStore } from './interfaces/aard-canvas-store.interface';
import { AardDetectionSample, generateSampleArray, resetSamples } from './interfaces/aard-detection-sample.interface'; import { AardDetectionSample, generateSampleArray, resetSamples } from './interfaces/aard-detection-sample.interface';
import { AardStatus, initAardStatus } from './interfaces/aard-status.interface'; import { AardStatus, initAardStatus } from './interfaces/aard-status.interface';
import { AardTestResults, initAardTestResults, resetAardTestResults } from './interfaces/aard-test-results.interface'; import { AardTestResults, initAardTestResults, resetAardTestResults, resetGuardLine } from './interfaces/aard-test-results.interface';
import { AardTimers, initAardTimers } from './interfaces/aard-timers.interface'; import { AardTimers, initAardTimers } from './interfaces/aard-timers.interface';
@ -222,9 +222,9 @@ export class Aard {
private arid: string; private arid: string;
private eventBusCommands = { private eventBusCommands = {
// 'get-aard-timing': [{ // 'get-aard-timing': {
// function: () => this.handlePerformanceDataRequest() // function: () => this.handlePerformanceDataRequest()
// }] // }
}; };
//#endregion //#endregion
@ -240,6 +240,8 @@ export class Aard {
private canvasStore: AardCanvasStore; private canvasStore: AardCanvasStore;
private testResults: AardTestResults; private testResults: AardTestResults;
private canvasSamples: AardDetectionSample; private canvasSamples: AardDetectionSample;
private forceFullRecheck: boolean = true;
//#endregion //#endregion
//#region getters //#region getters
@ -267,7 +269,8 @@ export class Aard {
this.settings = videoData.settings; this.settings = videoData.settings;
this.eventBus = videoData.eventBus; this.eventBus = videoData.eventBus;
this.initEventBus(); this.eventBus.subscribeMulti(this.eventBusCommands, this);
this.arid = (Math.random()*100).toFixed(); this.arid = (Math.random()*100).toFixed();
// we can tick manually, for debugging // we can tick manually, for debugging
@ -276,21 +279,12 @@ export class Aard {
this.init(); this.init();
} }
private initEventBus() {
for (const action in this.eventBusCommands) {
for (const command of this.eventBusCommands[action]) {
this.eventBus.subscribe(action, command);
}
}
}
/** /**
* Initializes Aard with default values and starts autodetection loop. * Initializes Aard with default values and starts autodetection loop.
* This method should only ever be called from constructor. * This method should only ever be called from constructor.
*/ */
private init() { private init() {
this.canvasStore = { this.canvasStore = {
main: this.createCanvas('main-gl') main: this.createCanvas('main-gl')
}; };
@ -351,6 +345,7 @@ export class Aard {
* Starts autodetection loop. * Starts autodetection loop.
*/ */
start() { start() {
this.forceFullRecheck = true;
if (this.videoData.resizer.lastAr.type === AspectRatioType.AutomaticUpdate) { if (this.videoData.resizer.lastAr.type === AspectRatioType.AutomaticUpdate) {
// ensure first autodetection will run in any case // ensure first autodetection will run in any case
this.videoData.resizer.lastAr = {type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr}; this.videoData.resizer.lastAr = {type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr};
@ -406,11 +401,14 @@ export class Aard {
} }
this.status.lastVideoStatus = videoState; this.status.lastVideoStatus = videoState;
if (Date.now() < this.timers.nextFrameCheckTime) { const now = Date.now();
if (now < (this.videoData.player.isTooSmall ? this.timers.reducedPollingNextCheckTime : this.timers.nextFrameCheckTime)) {
return false; return false;
} }
this.timers.nextFrameCheckTime = Date.now() + this.settings.active.arDetect.timers.playing; this.timers.nextFrameCheckTime = now + this.settings.active.arDetect.timers.playing;
this.timers.reducedPollingNextCheckTime = now + this.settings.active.arDetect.timers.playingReduced;
return true; return true;
} }
@ -419,6 +417,7 @@ export class Aard {
resetAardTestResults(this.testResults); resetAardTestResults(this.testResults);
resetSamples(this.canvasSamples); resetSamples(this.canvasSamples);
this.main(); this.main();
this.forceFullRecheck = false;
} else { } else {
} }
this.animationFrame = window.requestAnimationFrame( (ts: DOMHighResTimeStamp) => this.onAnimationFrame(ts)); this.animationFrame = window.requestAnimationFrame( (ts: DOMHighResTimeStamp) => this.onAnimationFrame(ts));
@ -477,8 +476,13 @@ export class Aard {
); );
if (this.testResults.notLetterbox) { if (this.testResults.notLetterbox) {
// TODO: reset aspect ratio to "AR not applied" // TODO: reset aspect ratio to "AR not applied"
console.log('NOT LETTERBOX!');
this.testResults.lastStage = 1; this.testResults.lastStage = 1;
// we have a few things to do
// console.log('NOT LETTERBOX - resetting letterbox data')
this.testResults.letterboxWidth = 0;
this.testResults.letterboxOffset = 0;
resetGuardLine(this.testResults);
break; break;
} }
@ -486,19 +490,36 @@ export class Aard {
// Check if previously detected aspect ratio is still gucci. If it is, then // Check if previously detected aspect ratio is still gucci. If it is, then
// we can quit the loop without applying any aspect ratios (unless subtitle // we can quit the loop without applying any aspect ratios (unless subtitle
// detection is enabled, in which case we still run the subtitle test) // detection is enabled, in which case we still run the subtitle test)
// If we stopped autodetection because of manual aspect ratio input, then
// checkLetterboxShrink and checkLetterboxGrow may return invalid results.
// This is why we skip this check and force full recheck if forceFullRecheck
// flag is set.
if (this.forceFullRecheck) {
this.testResults.imageLine.invalidated = true;
this.testResults.guardLine.invalidated = true;
} else {
this.checkLetterboxShrink( this.checkLetterboxShrink(
imageData, imageData,
this.settings.active.arDetect.canvasDimensions.sampleCanvas.width, this.settings.active.arDetect.canvasDimensions.sampleCanvas.width,
this.settings.active.arDetect.canvasDimensions.sampleCanvas.height this.settings.active.arDetect.canvasDimensions.sampleCanvas.height
); );
console.log('LETTERBOX SHRINK CHECK RESULT — IS GUARDLINE INVALIDATED?', this.testResults.guardLine.invalidated)
if (! this.testResults.guardLine.invalidated) { // If guardline was invalidated, letterbox width and offset are unreliable.
// If guardLine is fine but imageLine is invalidated, we still keep last letterbox settings
if (this.testResults.guardLine.invalidated) {
// console.log('GUARD LINE INVALIDATED - resetting letterbox data')
this.testResults.letterboxWidth = 0;
this.testResults.letterboxOffset = 0;
} else {
this.checkLetterboxGrow( this.checkLetterboxGrow(
imageData, imageData,
this.settings.active.arDetect.canvasDimensions.sampleCanvas.width, this.settings.active.arDetect.canvasDimensions.sampleCanvas.width,
this.settings.active.arDetect.canvasDimensions.sampleCanvas.height this.settings.active.arDetect.canvasDimensions.sampleCanvas.height
); );
} }
}
// Both need to be checked // Both need to be checked
if (! (this.testResults.imageLine.invalidated || this.testResults.guardLine.invalidated)) { if (! (this.testResults.imageLine.invalidated || this.testResults.guardLine.invalidated)) {
// TODO: ensure no aspect ratio changes happen // TODO: ensure no aspect ratio changes happen
@ -522,10 +543,21 @@ export class Aard {
// Also note that subtitle check should run on newest aspect ratio data, rather than lag one frame behind // Also note that subtitle check should run on newest aspect ratio data, rather than lag one frame behind
// But implementation details are something for future Tam to figure out // But implementation details are something for future Tam to figure out
// if detection is uncertain, we don't do anything at all // If forceFullRecheck is set, then 'not letterbox' should always force-reset the aspect ratio
if (this.testResults.aspectRatioUncertain) { // (as aspect ratio may have been set manually while autodetection was off)
console.info('aspect ratio not cettain.'); if (this.testResults.notLetterbox) {
console.warn('check finished:', JSON.parse(JSON.stringify(this.testResults)), JSON.parse(JSON.stringify(this.canvasSamples)), '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'); // console.log('————not letterbox')
this.updateAspectRatio(this.defaultAr);
}
// if detection is uncertain, we don't do anything at all (unless if guardline was broken, in which case we reset)
if (this.testResults.aspectRatioUncertain && this.testResults.guardLine.invalidated) {
// console.info('aspect ratio not certain:', this.testResults.aspectRatioUncertainReason);
// console.warn('check finished:', JSON.parse(JSON.stringify(this.testResults)), JSON.parse(JSON.stringify(this.canvasSamples)), '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n');
if (this.testResults.guardLine.invalidated) {
this.updateAspectRatio(this.defaultAr);
}
return; return;
} }
@ -533,22 +565,27 @@ export class Aard {
// TODO: emit debug values if debugging is enabled // TODO: emit debug values if debugging is enabled
this.testResults.isFinished = true; this.testResults.isFinished = true;
console.warn('check finished:', JSON.parse(JSON.stringify(this.testResults)), JSON.parse(JSON.stringify(this.canvasSamples)), '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'); // console.warn(
// `[${(+new Date() % 10000) / 100} | ${this.arid}]`,'check finished — aspect ratio updated:', this.testResults.aspectRatioUpdated,
// '\ndetected ar:', this.testResults.activeAspectRatio, '->', this.getAr(),
// '\nis video playing?', this.getVideoPlaybackState() === VideoPlaybackState.Playing,
// '\n\n', JSON.parse(JSON.stringify(this.testResults)), JSON.parse(JSON.stringify(this.canvasSamples)), '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n');
// if edge width changed, emit update event. // if edge width changed, emit update event.
if (this.testResults.aspectRatioUpdated) { // except aspectRatioUpdated doesn't get set reliably, so we just call update every time, and update
this.videoData.resizer.updateAr({ // if detected aspect ratio is different from the current aspect ratio
type: AspectRatioType.AutomaticUpdate, // if (this.testResults.aspectRatioUpdated) {
ratio: this.getAr(), this.updateAspectRatio();
offset: this.testResults.letterboxOffset // }
});
} // if we got "no letterbox" OR aspectRatioUpdated
} catch (e) { } catch (e) {
console.warn('[Ultrawidify] Aspect ratio autodetection crashed for some reason.\n\nsome reason:', e); console.warn('[Ultrawidify] Aspect ratio autodetection crashed for some reason.\n\nsome reason:', e);
this.videoData.resizer.setAr({type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr}); this.videoData.resizer.setAr({type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr});
} }
} }
private getVideoPlaybackState(): VideoPlaybackState { private getVideoPlaybackState(): VideoPlaybackState {
try { try {
if (this.video.ended) { if (this.video.ended) {
@ -677,6 +714,8 @@ export class Aard {
// NOTE: but maybe we could, if blackLevel can only get lower than // NOTE: but maybe we could, if blackLevel can only get lower than
// the default value. // the default value.
if (this.testResults.notLetterbox) { if (this.testResults.notLetterbox) {
this.testResults.aspectRatioUncertain = false;
if (min < this.testResults.blackLevel) { if (min < this.testResults.blackLevel) {
this.testResults.blackLevel = min; this.testResults.blackLevel = min;
this.testResults.blackThreshold = min + 16; this.testResults.blackThreshold = min + 16;
@ -715,6 +754,7 @@ export class Aard {
const segmentPixels = width * edgePosition; const segmentPixels = width * edgePosition;
const edgeSegmentSize = segmentPixels * 4; const edgeSegmentSize = segmentPixels * 4;
// check the top // check the top
{ {
// no use in doing guardline tests if guardline hasn't been measured yet, or if // no use in doing guardline tests if guardline hasn't been measured yet, or if
@ -725,6 +765,7 @@ export class Aard {
const secondSegment = rowEnd - edgeSegmentSize; const secondSegment = rowEnd - edgeSegmentSize;
let i = rowStart; let i = rowStart;
while (i < firstSegment) { while (i < firstSegment) {
if ( if (
imageData[i] > this.testResults.blackThreshold imageData[i] > this.testResults.blackThreshold
@ -815,8 +856,6 @@ export class Aard {
const maxViolations = segmentPixels * 0.20; // TODO: move the 0.2 threshold into settings const maxViolations = segmentPixels * 0.20; // TODO: move the 0.2 threshold into settings
console.log('Corner violations counts — segment px & max violations,', segmentPixels, maxViolations )
// we won't do a loop for this few elements // we won't do a loop for this few elements
// corners with stuff in them will also be skipped in image test // corners with stuff in them will also be skipped in image test
this.testResults.guardLine.cornerViolated[0] = this.testResults.guardLine.cornerPixelsViolated[0] > maxViolations; this.testResults.guardLine.cornerViolated[0] = this.testResults.guardLine.cornerPixelsViolated[0] > maxViolations;
@ -824,8 +863,9 @@ export class Aard {
this.testResults.guardLine.cornerViolated[2] = this.testResults.guardLine.cornerPixelsViolated[2] > maxViolations; this.testResults.guardLine.cornerViolated[2] = this.testResults.guardLine.cornerPixelsViolated[2] > maxViolations;
this.testResults.guardLine.cornerViolated[3] = this.testResults.guardLine.cornerPixelsViolated[3] > maxViolations; this.testResults.guardLine.cornerViolated[3] = this.testResults.guardLine.cornerPixelsViolated[3] > maxViolations;
const maxInvalidCorners = 1; // TODO: move this into settings — by default, we allow one corner to extend past the const maxInvalidCorners = 0; // TODO: move this into settings — by default, we allow one corner to extend past the
// guard line in order to prevent watermarks/logos from preventing cropping the video // guard line in order to prevent watermarks/logos from preventing cropping the video
// .... _except_ this doesn't really work because https://youtu.be/-YJwPXipJbo?t=459
// this works because +true converts to 1 and +false converts to 0 // this works because +true converts to 1 and +false converts to 0
const dirtyCount = +this.testResults.guardLine.cornerViolated[0] const dirtyCount = +this.testResults.guardLine.cornerViolated[0]
@ -1085,12 +1125,7 @@ export class Aard {
// fact that it makes the 'if' statement governing gradient detection // fact that it makes the 'if' statement governing gradient detection
// bit more nicely visible (instead of hidden among spagheti) // bit more nicely visible (instead of hidden among spagheti)
this.edgeScan(imageData, width, height); this.edgeScan(imageData, width, height);
console.log('edge scan:', JSON.parse(JSON.stringify(this.canvasSamples)));
this.validateEdgeScan(imageData, width, height); this.validateEdgeScan(imageData, width, height);
console.log('edge scan post valid:', JSON.parse(JSON.stringify(this.canvasSamples)));
// TODO: _if gradient detection is enabled, then: // TODO: _if gradient detection is enabled, then:
this.sampleForGradient(imageData, width, height); this.sampleForGradient(imageData, width, height);
@ -1122,18 +1157,20 @@ export class Aard {
* about where to find our letterbox. This test is all the data we need to check * about where to find our letterbox. This test is all the data we need to check
* if valid guardLine has ever been set, since guardLine and imageLine are set * if valid guardLine has ever been set, since guardLine and imageLine are set
* in tandem (either both exist, or neither does (-1)). * in tandem (either both exist, or neither does (-1)).
*
* But maybe we _can't really_, because https://youtu.be/-YJwPXipJbo?t=460 is having problems detecting change
*/ */
if (this.testResults.guardLine.top > 0) { // if (this.testResults.guardLine.top > 0) {
// if guardLine is invalidated, then the new edge of image frame must be // // if guardLine is invalidated, then the new edge of image frame must be
// above former guardline. Otherwise, it's below it. // // above former guardline. Otherwise, it's below it.
if (this.testResults.guardLine.invalidated) { // if (this.testResults.guardLine.invalidated) {
topEnd = this.testResults.guardLine.top; // topEnd = this.testResults.guardLine.top;
bottomEnd = this.testResults.guardLine.bottom; // bottomEnd = this.testResults.guardLine.bottom;
} else { // } else {
topStart = this.testResults.imageLine.top; // topStart = this.testResults.imageLine.top;
bottomStart = this.testResults.imageLine.bottom; // bottomStart = this.testResults.imageLine.bottom;
} // }
} // }
let row: number, i: number, x: number, isImage: boolean, finishedRows: number; let row: number, i: number, x: number, isImage: boolean, finishedRows: number;
@ -1356,7 +1393,6 @@ export class Aard {
// didn't change meaningfully from the first, in which chance we aren't. If the brightness increased // didn't change meaningfully from the first, in which chance we aren't. If the brightness increased
// anywhere between 'not enough' and 'too much', we mark the measurement as invalid. // anywhere between 'not enough' and 'too much', we mark the measurement as invalid.
if (lastSubpixel - firstSubpixel > this.settings.active.arDetect.edgeDetection.gradientTestMinDelta) { if (lastSubpixel - firstSubpixel > this.settings.active.arDetect.edgeDetection.gradientTestMinDelta) {
console.log('sample invalidated cus gradient:');
this.canvasSamples.top[i] = -1; this.canvasSamples.top[i] = -1;
} }
} }
@ -1442,6 +1478,8 @@ export class Aard {
// remember: array has two places per sample position — hence x2 on the results // remember: array has two places per sample position — hence x2 on the results
const leftEdgeBoundary = ~~(fullFence * edgePosition) * 2; const leftEdgeBoundary = ~~(fullFence * edgePosition) * 2;
const rightEdgeBoundary = (this.settings.active.arDetect.sampling.staticCols - leftEdgeBoundary) * 2; const rightEdgeBoundary = (this.settings.active.arDetect.sampling.staticCols - leftEdgeBoundary) * 2;
const edgeTolerance = this.settings.active.arDetect.edgeDetection.edgeMismatchTolerancePx;
let i: number; let i: number;
// Process top edge: // Process top edge:
@ -1457,7 +1495,7 @@ export class Aard {
while (i < leftEdgeBoundary) { while (i < leftEdgeBoundary) {
if (this.canvasSamples.top[i] > -1) { if (this.canvasSamples.top[i] > -1) {
if (this.canvasSamples.top[i] <= this.testResults.aspectRatioCheck.topRows[0]) { if (this.canvasSamples.top[i] < this.testResults.aspectRatioCheck.topRows[0]) {
this.testResults.aspectRatioCheck.topRows[0] = this.canvasSamples.top[i]; this.testResults.aspectRatioCheck.topRows[0] = this.canvasSamples.top[i];
this.testResults.aspectRatioCheck.topQuality[0] = 0; this.testResults.aspectRatioCheck.topQuality[0] = 0;
} else if (this.canvasSamples.top[i] === this.testResults.aspectRatioCheck.topRows[0]) { } else if (this.canvasSamples.top[i] === this.testResults.aspectRatioCheck.topRows[0]) {
@ -1469,7 +1507,7 @@ export class Aard {
while (i < rightEdgeBoundary) { while (i < rightEdgeBoundary) {
if (this.canvasSamples.top[i] > -1) { if (this.canvasSamples.top[i] > -1) {
if (this.canvasSamples.top[i] <= this.testResults.aspectRatioCheck.topRows[1]) { if (this.canvasSamples.top[i] < this.testResults.aspectRatioCheck.topRows[1]) {
this.testResults.aspectRatioCheck.topRows[1] = this.canvasSamples.top[i]; this.testResults.aspectRatioCheck.topRows[1] = this.canvasSamples.top[i];
this.testResults.aspectRatioCheck.topQuality[1] = 0; this.testResults.aspectRatioCheck.topQuality[1] = 0;
} else if (this.canvasSamples.top[i] === this.testResults.aspectRatioCheck.topRows[1]) { } else if (this.canvasSamples.top[i] === this.testResults.aspectRatioCheck.topRows[1]) {
@ -1481,7 +1519,7 @@ export class Aard {
while (i < this.canvasSamples.top.length) { while (i < this.canvasSamples.top.length) {
if (this.canvasSamples.top[i] > -1) { if (this.canvasSamples.top[i] > -1) {
if (this.canvasSamples.top[i] <= this.testResults.aspectRatioCheck.topRows[2]) { if (this.canvasSamples.top[i] < this.testResults.aspectRatioCheck.topRows[2]) {
this.testResults.aspectRatioCheck.topRows[2] = this.canvasSamples.top[i]; this.testResults.aspectRatioCheck.topRows[2] = this.canvasSamples.top[i];
this.testResults.aspectRatioCheck.topQuality[2] = 0; this.testResults.aspectRatioCheck.topQuality[2] = 0;
} else if (this.canvasSamples.top[i] === this.testResults.aspectRatioCheck.topRows[2]) { } else if (this.canvasSamples.top[i] === this.testResults.aspectRatioCheck.topRows[2]) {
@ -1490,6 +1528,17 @@ export class Aard {
} }
i += 2; i += 2;
} }
// remove any stray infinities
if (this.testResults.aspectRatioCheck.topRows[0] === Infinity) {
this.testResults.aspectRatioCheck.topRows[0] = 0;
}
if (this.testResults.aspectRatioCheck.topRows[1] === Infinity) {
this.testResults.aspectRatioCheck.topRows[1] = 0;
}
if (this.testResults.aspectRatioCheck.topRows[2] === Infinity) {
this.testResults.aspectRatioCheck.topRows[2] = 0;
}
} }
// Process bottom edge // Process bottom edge
@ -1505,7 +1554,7 @@ export class Aard {
while (i < leftEdgeBoundary) { while (i < leftEdgeBoundary) {
if (this.canvasSamples.bottom[i] > -1) { if (this.canvasSamples.bottom[i] > -1) {
if (this.canvasSamples.bottom[i] <= this.testResults.aspectRatioCheck.bottomRows[0]) { if (this.canvasSamples.bottom[i] < this.testResults.aspectRatioCheck.bottomRows[0]) {
this.testResults.aspectRatioCheck.bottomRows[0] = this.canvasSamples.bottom[i]; this.testResults.aspectRatioCheck.bottomRows[0] = this.canvasSamples.bottom[i];
this.testResults.aspectRatioCheck.bottomQuality[0] = 0; this.testResults.aspectRatioCheck.bottomQuality[0] = 0;
} else if (this.canvasSamples.bottom[i] === this.testResults.aspectRatioCheck.bottomRows[0]) { } else if (this.canvasSamples.bottom[i] === this.testResults.aspectRatioCheck.bottomRows[0]) {
@ -1517,7 +1566,7 @@ export class Aard {
while (i < rightEdgeBoundary) { while (i < rightEdgeBoundary) {
if (this.canvasSamples.bottom[i] > -1) { if (this.canvasSamples.bottom[i] > -1) {
if (this.canvasSamples.bottom[i] <= this.testResults.aspectRatioCheck.bottomRows[1]) { if (this.canvasSamples.bottom[i] < this.testResults.aspectRatioCheck.bottomRows[1]) {
this.testResults.aspectRatioCheck.bottomRows[1] = this.canvasSamples.bottom[i]; this.testResults.aspectRatioCheck.bottomRows[1] = this.canvasSamples.bottom[i];
this.testResults.aspectRatioCheck.bottomQuality[1] = 0; this.testResults.aspectRatioCheck.bottomQuality[1] = 0;
} else if (this.canvasSamples.bottom[i] === this.testResults.aspectRatioCheck.bottomRows[1]) { } else if (this.canvasSamples.bottom[i] === this.testResults.aspectRatioCheck.bottomRows[1]) {
@ -1529,7 +1578,7 @@ export class Aard {
while (i < this.canvasSamples.bottom.length) { while (i < this.canvasSamples.bottom.length) {
if (this.canvasSamples.bottom[i] > -1) { if (this.canvasSamples.bottom[i] > -1) {
if (this.canvasSamples.bottom[i] <= this.testResults.aspectRatioCheck.bottomRows[2]) { if (this.canvasSamples.bottom[i] < this.testResults.aspectRatioCheck.bottomRows[2]) {
this.testResults.aspectRatioCheck.bottomRows[2] = this.canvasSamples.bottom[i]; this.testResults.aspectRatioCheck.bottomRows[2] = this.canvasSamples.bottom[i];
this.testResults.aspectRatioCheck.bottomQuality[2] = 0; this.testResults.aspectRatioCheck.bottomQuality[2] = 0;
} else if (this.canvasSamples.bottom[i] === this.testResults.aspectRatioCheck.bottomRows[2]) { } else if (this.canvasSamples.bottom[i] === this.testResults.aspectRatioCheck.bottomRows[2]) {
@ -1578,59 +1627,63 @@ export class Aard {
*/ */
// TOP: // TOP:
if (
this.testResults.aspectRatioCheck.topRows[0] === this.testResults.aspectRatioCheck.topRows[1] // DifferenceMatrix:
&& this.testResults.aspectRatioCheck.topRows[0] === this.testResults.aspectRatioCheck.topRows[2] // 0 - center <> left
// 1 - center <> right
// 2 - left <> right
this.testResults.aspectRatioCheck.topRowsDifferenceMatrix[0] = Math.abs(this.testResults.aspectRatioCheck.topRows[1] - this.testResults.aspectRatioCheck.topRows[0]);
this.testResults.aspectRatioCheck.topRowsDifferenceMatrix[1] = Math.abs(this.testResults.aspectRatioCheck.topRows[1] - this.testResults.aspectRatioCheck.topRows[2]);
this.testResults.aspectRatioCheck.topRowsDifferenceMatrix[2] = Math.abs(this.testResults.aspectRatioCheck.topRows[0] - this.testResults.aspectRatioCheck.topRows[2]);
this.testResults.aspectRatioCheck.bottomRowsDifferenceMatrix[0] = Math.abs(this.testResults.aspectRatioCheck.bottomRows[0] - this.testResults.aspectRatioCheck.bottomRows[1]);
this.testResults.aspectRatioCheck.bottomRowsDifferenceMatrix[1] = Math.abs(this.testResults.aspectRatioCheck.bottomRows[1] - this.testResults.aspectRatioCheck.bottomRows[2]);
this.testResults.aspectRatioCheck.bottomRowsDifferenceMatrix[2] = Math.abs(this.testResults.aspectRatioCheck.bottomRows[0] - this.testResults.aspectRatioCheck.bottomRows[2]);
// We need to write if-statements in order of importance.
if ( // BEST: center matches both corners
this.testResults.aspectRatioCheck.topRowsDifferenceMatrix[0] <= edgeTolerance
&& this.testResults.aspectRatioCheck.topRowsDifferenceMatrix[1] <= edgeTolerance
) { ) {
// All three detections are the same
this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[0]; this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[0];
this.testResults.aspectRatioCheck.topCandidateQuality = this.testResults.aspectRatioCheck.topCandidateQuality =
this.testResults.aspectRatioCheck.topQuality[0] this.testResults.aspectRatioCheck.topQuality[0]
+ this.testResults.aspectRatioCheck.topQuality[1] + this.testResults.aspectRatioCheck.topQuality[1]
+ this.testResults.aspectRatioCheck.topQuality[2]; + this.testResults.aspectRatioCheck.topQuality[2];
} else if (this.testResults.aspectRatioCheck.topRows[0] === this.testResults.aspectRatioCheck.topRows[2]) { } else if ( // Second best: center matches one of the corners
// Corners are the same, but different from center this.testResults.aspectRatioCheck.topRowsDifferenceMatrix[0] <= edgeTolerance
if (this.testResults.aspectRatioCheck.topRows[0] > this.testResults.aspectRatioCheck.topRows[1]) { || this.testResults.aspectRatioCheck.topRowsDifferenceMatrix[1] <= edgeTolerance
// Corners are above center. ) {
this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[1];
this.testResults.aspectRatioCheck.topCandidateQuality = this.testResults.aspectRatioCheck.topQuality[1];
if (this.testResults.aspectRatioCheck.topRows[0] === this.testResults.aspectRatioCheck.topRows[1]) {
this.testResults.aspectRatioCheck.topCandidateQuality += this.testResults.aspectRatioCheck.topQuality[0];
} else {
this.testResults.aspectRatioCheck.topCandidateQuality += this.testResults.aspectRatioCheck.topQuality[2];
}
} else if (this.testResults.aspectRatioCheck.topRowsDifferenceMatrix[2] <= edgeTolerance) { // Third best: corners match, but are different from center
if (this.testResults.aspectRatioCheck.topRows[0] < this.testResults.aspectRatioCheck.topRows[1]) {
// Corners are above center -> corner authority
this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[0]; this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[0];
this.testResults.aspectRatioCheck.topCandidateQuality = this.testResults.aspectRatioCheck.topCandidateQuality =
this.testResults.aspectRatioCheck.topQuality[0] this.testResults.aspectRatioCheck.topQuality[0]
+ this.testResults.aspectRatioCheck.topQuality[2] + this.testResults.aspectRatioCheck.topQuality[2]
} else { } else {
// Corners are below center // Corners are below center - center authority
this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[1]; this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[1];
this.testResults.aspectRatioCheck.topCandidateQuality = this.testResults.aspectRatioCheck.topQuality[1] this.testResults.aspectRatioCheck.topCandidateQuality = this.testResults.aspectRatioCheck.topQuality[1]
} }
} else { } else { // Worst: no matches, kinda like my tinder
// Corners are different. this.testResults.topRowUncertain = true;
if ( // we can second-wind this, so no returns yet.
this.testResults.aspectRatioCheck.topRows[0] !== this.testResults.aspectRatioCheck.topRows[1]
&& this.testResults.aspectRatioCheck.topRows[2] !== this.testResults.aspectRatioCheck.topRows[1]
) {
// Center and matches neither of the corners.
// TODO: maybe we can figure out to guess aspect ratio in scenarios like this.
// But for the time being, just slap it with "inconclusive".
this.testResults.aspectRatioUncertain = true;
return;
} else {
// center matches one of the corners
this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[1];
this.testResults.aspectRatioCheck.topCandidateQuality = this.testResults.aspectRatioCheck.topQuality[1];
if (this.testResults.aspectRatioCheck.topRows[0] === this.testResults.aspectRatioCheck.topRows[1]) {
this.testResults.aspectRatioCheck.topCandidateQuality += this.testResults.aspectRatioCheck.topRows[0];
} else {
this.testResults.aspectRatioCheck.topCandidateQuality += this.testResults.aspectRatioCheck.topRows[2];
}
}
} }
// BOTTOM // BOTTOM
// Note that bottomRows candidates are measured from the top // Note that bottomRows candidates are measured from the top
// Well have to invert our candidate after we're done // Well have to invert our candidate after we're done
if ( if ( // BEST: center matches both corners
this.testResults.aspectRatioCheck.bottomRows[0] === this.testResults.aspectRatioCheck.bottomRows[1] this.testResults.aspectRatioCheck.bottomRowsDifferenceMatrix[0] <= edgeTolerance
&& this.testResults.aspectRatioCheck.bottomRows[0] === this.testResults.aspectRatioCheck.bottomRows[2] && this.testResults.aspectRatioCheck.bottomRowsDifferenceMatrix[1] <= edgeTolerance
) { ) {
// All three detections are the same // All three detections are the same
this.testResults.aspectRatioCheck.bottomCandidate = this.testResults.aspectRatioCheck.bottomRows[0]; this.testResults.aspectRatioCheck.bottomCandidate = this.testResults.aspectRatioCheck.bottomRows[0];
@ -1638,46 +1691,77 @@ export class Aard {
this.testResults.aspectRatioCheck.bottomQuality[0] this.testResults.aspectRatioCheck.bottomQuality[0]
+ this.testResults.aspectRatioCheck.bottomQuality[1] + this.testResults.aspectRatioCheck.bottomQuality[1]
+ this.testResults.aspectRatioCheck.bottomQuality[2]; + this.testResults.aspectRatioCheck.bottomQuality[2];
} else if (this.testResults.aspectRatioCheck.bottomRows[0] === this.testResults.aspectRatioCheck.bottomRows[2]) { } else if ( // Second best: center matches one of the corners
// Corners are the same, but different from center this.testResults.aspectRatioCheck.bottomRowsDifferenceMatrix[0] <= edgeTolerance
|| this.testResults.aspectRatioCheck.bottomRowsDifferenceMatrix[1] <= edgeTolerance
) {
this.testResults.aspectRatioCheck.bottomCandidate = this.testResults.aspectRatioCheck.bottomRows[1];
this.testResults.aspectRatioCheck.bottomCandidateQuality = this.testResults.aspectRatioCheck.bottomQuality[1];
if (this.testResults.aspectRatioCheck.bottomRows[0] === this.testResults.aspectRatioCheck.bottomRows[1]) {
this.testResults.aspectRatioCheck.bottomCandidateQuality += this.testResults.aspectRatioCheck.bottomQuality[0];
} else {
this.testResults.aspectRatioCheck.bottomCandidateQuality += this.testResults.aspectRatioCheck.bottomQuality[2];
}
} else if (this.testResults.aspectRatioCheck.bottomRowsDifferenceMatrix[2] <= edgeTolerance) { // Third best: corners match, but are different from center
if (this.testResults.aspectRatioCheck.bottomRows[0] > this.testResults.aspectRatioCheck.bottomRows[1]) { if (this.testResults.aspectRatioCheck.bottomRows[0] > this.testResults.aspectRatioCheck.bottomRows[1]) {
// Corners are above center. // Corners closer to the edges than center. Note that bigger number = closer to edge
this.testResults.aspectRatioCheck.bottomCandidate = this.testResults.aspectRatioCheck.bottomRows[0]; this.testResults.aspectRatioCheck.bottomCandidate = this.testResults.aspectRatioCheck.bottomRows[0];
this.testResults.aspectRatioCheck.bottomCandidateQuality = this.testResults.aspectRatioCheck.bottomCandidateQuality =
this.testResults.aspectRatioCheck.bottomQuality[0] this.testResults.aspectRatioCheck.bottomQuality[0]
+ this.testResults.aspectRatioCheck.bottomQuality[2] + this.testResults.aspectRatioCheck.bottomQuality[2]
} else { } else {
// Corners are below center // Center is closer to the edge than corners
this.testResults.aspectRatioCheck.bottomCandidate = this.testResults.aspectRatioCheck.bottomRows[1]; this.testResults.aspectRatioCheck.bottomCandidate = this.testResults.aspectRatioCheck.bottomRows[1];
this.testResults.aspectRatioCheck.bottomCandidateQuality = this.testResults.aspectRatioCheck.bottomQuality[1] this.testResults.aspectRatioCheck.bottomCandidateQuality = this.testResults.aspectRatioCheck.bottomQuality[1]
} }
} else { } else { // Worst: nothing matches
// Corners are different. // We'll try to figure out aspect ratio later in second wind
if ( this.testResults.bottomRowUncertain = true;
this.testResults.aspectRatioCheck.bottomRows[0] !== this.testResults.aspectRatioCheck.bottomRows[1]
&& this.testResults.aspectRatioCheck.bottomRows[2] !== this.testResults.aspectRatioCheck.bottomRows[1]
) {
// Center and matches neither of the corners.
// TODO: maybe we can figure out to guess aspect ratio in scenarios like this.
// But for the time being, just slap it with "inconclusive".
this.testResults.aspectRatioUncertain = true;
return;
} else {
// center matches one of the corners
this.testResults.aspectRatioCheck.bottomCandidate = this.testResults.aspectRatioCheck.bottomRows[1];
this.testResults.aspectRatioCheck.bottomCandidateQuality = this.testResults.aspectRatioCheck.bottomQuality[1];
if (this.testResults.aspectRatioCheck.bottomRows[0] === this.testResults.aspectRatioCheck.bottomRows[1]) { // console.log('BOTTOM ROW MISMATCH:', this.testResults.aspectRatioCheck.bottomRows[0], this.testResults.aspectRatioCheck.bottomRows[1], this.testResults.aspectRatioCheck.bottomRows[2]);
this.testResults.aspectRatioCheck.bottomCandidateQuality += this.testResults.aspectRatioCheck.bottomRows[0]; // return;
} else {
this.testResults.aspectRatioCheck.bottomCandidateQuality += this.testResults.aspectRatioCheck.bottomRows[2];
}
} }
if (this.testResults.topRowUncertain && this.testResults.bottomRowUncertain) {
this.testResults.aspectRatioUncertain = true;
this.testResults.aspectRatioUncertainReason = 'TOP_AND_BOTTOM_ROW_MISMATCH';
} }
// Convert bottom candidate to letterbox width // Convert bottom candidate to letterbox width
this.testResults.aspectRatioCheck.bottomCandidateDistance = this.testResults.aspectRatioCheck.bottomCandidate === Infinity ? -1 : height - this.testResults.aspectRatioCheck.bottomCandidate; this.testResults.aspectRatioCheck.bottomCandidateDistance = this.testResults.aspectRatioCheck.bottomCandidate === Infinity ? -1 : height - this.testResults.aspectRatioCheck.bottomCandidate;
const maxOffset = ~~(height * this.settings.active.arDetect.edgeDetection.maxLetterboxOffset)
// attempt second-wind:
// if any of the top candidates matches the best bottom candidate sufficiently,
// we'll just promote it to the candidate status
if (this.testResults.topRowUncertain) {
if (this.testResults.aspectRatioCheck.bottomCandidateDistance - this.testResults.aspectRatioCheck.topRows[0] < edgeTolerance + maxOffset) {
this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[0];
this.testResults.aspectRatioCheck.topCandidateQuality = this.testResults.aspectRatioCheck.topQuality[0] + this.testResults.aspectRatioCheck.bottomCandidateQuality;
} else if (this.testResults.aspectRatioCheck.bottomCandidateDistance - this.testResults.aspectRatioCheck.topRows[1] < edgeTolerance + maxOffset) {
this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[1];
this.testResults.aspectRatioCheck.topCandidateQuality = this.testResults.aspectRatioCheck.topQuality[1] + this.testResults.aspectRatioCheck.bottomCandidateQuality;
} else if (this.testResults.aspectRatioCheck.bottomCandidateDistance - this.testResults.aspectRatioCheck.topRows[2] < edgeTolerance + maxOffset) {
this.testResults.aspectRatioCheck.topCandidate = this.testResults.aspectRatioCheck.topRows[2];
this.testResults.aspectRatioCheck.topCandidateQuality = this.testResults.aspectRatioCheck.topQuality[2] + this.testResults.aspectRatioCheck.bottomCandidateQuality;
}
} else if (this.testResults.bottomRowUncertain) {
const bottomEdgeEquivalent = height - this.testResults.aspectRatioCheck.topCandidate;
if (bottomEdgeEquivalent - this.testResults.aspectRatioCheck.bottomRows[0] < edgeTolerance + maxOffset) {
this.testResults.aspectRatioCheck.bottomCandidate = this.testResults.aspectRatioCheck.bottomRows[0];
this.testResults.aspectRatioCheck.bottomCandidateQuality = this.testResults.aspectRatioCheck.bottomQuality[0] + this.testResults.aspectRatioCheck.topCandidateQuality;
} else if (bottomEdgeEquivalent - this.testResults.aspectRatioCheck.bottomRows[1] < edgeTolerance + maxOffset) {
this.testResults.aspectRatioCheck.bottomCandidate = this.testResults.aspectRatioCheck.bottomRows[1];
this.testResults.aspectRatioCheck.bottomCandidateQuality = this.testResults.aspectRatioCheck.bottomQuality[1] + this.testResults.aspectRatioCheck.topCandidateQuality;
} else if (bottomEdgeEquivalent - this.testResults.aspectRatioCheck.bottomRows[2] < edgeTolerance + maxOffset) {
this.testResults.aspectRatioCheck.bottomCandidate = this.testResults.aspectRatioCheck.bottomRows[2];
this.testResults.aspectRatioCheck.bottomCandidateQuality = this.testResults.aspectRatioCheck.bottomQuality[2] + this.testResults.aspectRatioCheck.topCandidateQuality;
}
}
/** /**
* Get final results. * Get final results.
* Let candidateA hold better-quality candidate, and let the candidateB hold the lower-quality candidate. * Let candidateA hold better-quality candidate, and let the candidateB hold the lower-quality candidate.
@ -1697,15 +1781,16 @@ export class Aard {
|| candidateB < this.settings.active.arDetect.edgeDetection.thresholds.minQualitySecondEdge || candidateB < this.settings.active.arDetect.edgeDetection.thresholds.minQualitySecondEdge
) { ) {
this.testResults.aspectRatioUncertain = true; this.testResults.aspectRatioUncertain = true;
this.testResults.aspectRatioUncertainReason = 'INSUFFICIENT_EDGE_DETECTION_QUALITY';
return; return;
} }
const maxOffset = ~~(height * this.settings.active.arDetect.edgeDetection.maxLetterboxOffset)
const diff = this.testResults.aspectRatioCheck.topCandidate - this.testResults.aspectRatioCheck.bottomCandidateDistance; const diff = this.testResults.aspectRatioCheck.topCandidate - this.testResults.aspectRatioCheck.bottomCandidateDistance;
const candidateAvg = ~~((this.testResults.aspectRatioCheck.topCandidate + this.testResults.aspectRatioCheck.bottomCandidateDistance) / 2); const candidateAvg = ~~((this.testResults.aspectRatioCheck.topCandidate + this.testResults.aspectRatioCheck.bottomCandidateDistance) / 2);
if (diff > maxOffset) { if (diff > maxOffset) {
this.testResults.aspectRatioUncertain = true; this.testResults.aspectRatioUncertain = true;
this.testResults.aspectRatioUncertainReason = 'LETTERBOX_NOT_CENTERED_ENOUGH';
return; return;
} }
if (maxOffset > 2) { if (maxOffset > 2) {
@ -1714,12 +1799,35 @@ export class Aard {
this.testResults.guardLine.top = Math.max(this.testResults.imageLine.top - 2, 0); this.testResults.guardLine.top = Math.max(this.testResults.imageLine.top - 2, 0);
this.testResults.guardLine.bottom = Math.min(this.testResults.imageLine.bottom + 2, this.canvasStore.main.height - 1); this.testResults.guardLine.bottom = Math.min(this.testResults.imageLine.bottom + 2, this.canvasStore.main.height - 1);
} }
this.testResults.aspectRatioUncertain = false; this.testResults.aspectRatioUncertain = false;
console.log('Updating letterboxWidth - as normal')
this.testResults.letterboxWidth = candidateAvg; this.testResults.letterboxWidth = candidateAvg;
this.testResults.letterboxOffset = diff; this.testResults.letterboxOffset = diff;
this.testResults.aspectRatioUpdated = true; this.testResults.aspectRatioUpdated = true;
} }
/**
* Updates aspect ratio if new aspect ratio is different enough from the old one
*/
private updateAspectRatio(overrideAr?: number) {
const ar = overrideAr ?? this.getAr();
// Calculate difference between two ratios
const maxRatio = Math.max(ar, this.testResults.activeAspectRatio);
const diff = Math.abs(ar - this.testResults.activeAspectRatio);
if (overrideAr || (diff / maxRatio) > this.settings.active.arDetect.allowedArVariance) {
this.videoData.resizer.updateAr({
type: AspectRatioType.AutomaticUpdate,
ratio: this.getAr(),
offset: this.testResults.letterboxOffset
});
this.testResults.activeAspectRatio = ar;
}
}
/** /**
* Calculates video's current aspect ratio based on data in testResults. * Calculates video's current aspect ratio based on data in testResults.
* @returns * @returns
@ -1730,24 +1838,23 @@ export class Aard {
const compensatedWidth = fileAr === canvasAr ? this.canvasStore.main.width : this.canvasStore.main.width * fileAr; const compensatedWidth = fileAr === canvasAr ? this.canvasStore.main.width : this.canvasStore.main.width * fileAr;
// console.log(`
// ———— ASPECT RATIO CALCULATION: —————
console.log(` // canvas size: ${this.canvasStore.main.width} x ${this.canvasStore.main.height} (1:${this.canvasStore.main.width / this.canvasStore.main.height})
ASPECT RATIO CALCULATION: // file size: ${this.video.videoWidth} x ${this.video.videoHeight} (1:${this.video.videoWidth / this.video.videoHeight})
canvas size: ${this.canvasStore.main.width} x ${this.canvasStore.main.height} (1:${this.canvasStore.main.width / this.canvasStore.main.height}) // compensated size: ${compensatedWidth} x ${this.canvasStore.main.height} (1:${compensatedWidth / this.canvasStore.main.height})
file size: ${this.video.videoWidth} x ${this.video.videoHeight} (1:${this.video.videoWidth / this.video.videoHeight})
compensated size: ${compensatedWidth} x ${this.canvasStore.main.height} (1:${compensatedWidth / this.canvasStore.main.height}) // letterbox height: ${this.testResults.letterboxWidth}
// net video height: ${this.canvasStore.main.height - (this.testResults.letterboxWidth * 2)}
letterbox height: ${this.testResults.letterboxWidth} // calculated aspect ratio -----
net video height: ${this.canvasStore.main.height - (this.testResults.letterboxWidth * 2)}
calculated aspect ratio ----- // ${compensatedWidth} ${compensatedWidth} ${compensatedWidth}
// ——————————————— = —————————————— = —————— = ${compensatedWidth / (this.canvasStore.main.height - (this.testResults.letterboxWidth * 2))}
${compensatedWidth} ${compensatedWidth} ${compensatedWidth} // ${this.canvasStore.main.height} - 2 x ${this.testResults.letterboxWidth} ${this.canvasStore.main.height} - ${2 * this.testResults.letterboxWidth} ${this.canvasStore.main.height - (this.testResults.letterboxWidth * 2)}
= = = ${compensatedWidth / (this.canvasStore.main.height - (this.testResults.letterboxWidth * 2))} // `);
${this.canvasStore.main.height} - 2 x ${this.testResults.letterboxWidth} ${this.canvasStore.main.height} - ${2 * this.testResults.letterboxWidth} ${this.canvasStore.main.height - (this.testResults.letterboxWidth * 2)}
`);
return compensatedWidth / (this.canvasStore.main.height - (this.testResults.letterboxWidth * 2)); return compensatedWidth / (this.canvasStore.main.height - (this.testResults.letterboxWidth * 2));

View File

@ -0,0 +1,7 @@
enum AardMode {
Continuous = 0,
UntilDetection = 1,
Timeout
}
export default AardMode;

View File

@ -2,6 +2,7 @@ import {VideoPlaybackState} from '../enums/video-playback-state.enum';
export interface AardStatus { export interface AardStatus {
aardActive: boolean, aardActive: boolean,
aardReducedPolling: boolean,
checkInProgress: boolean, checkInProgress: boolean,
lastVideoStatus: VideoPlaybackState, lastVideoStatus: VideoPlaybackState,
@ -10,6 +11,7 @@ export interface AardStatus {
export function initAardStatus(): AardStatus { export function initAardStatus(): AardStatus {
return { return {
aardActive: false, aardActive: false,
aardReducedPolling: true,
checkInProgress: false, checkInProgress: false,
lastVideoStatus: VideoPlaybackState.NotInitialized, lastVideoStatus: VideoPlaybackState.NotInitialized,
} }

View File

@ -28,12 +28,18 @@ export interface AardTestResults {
bottomCandidate: number, bottomCandidate: number,
bottomCandidateDistance: number, bottomCandidateDistance: number,
bottomCandidateQuality: number, bottomCandidateQuality: number,
topRowsDifferenceMatrix: [number, number, number],
bottomRowsDifferenceMatrix: [number, number, number],
}, },
aspectRatioUncertain: boolean, aspectRatioUncertain: boolean,
topRowUncertain: boolean,
bottomRowUncertain: boolean,
aspectRatioUpdated: boolean, aspectRatioUpdated: boolean,
activeAspectRatio: number, // is cumulative
letterboxWidth: number, letterboxWidth: number,
letterboxOffset: number, letterboxOffset: number,
logoDetected: [boolean, boolean, boolean, boolean] logoDetected: [boolean, boolean, boolean, boolean]
aspectRatioUncertainReason?: string
} }
export function initAardTestResults(settings: AardSettings): AardTestResults { export function initAardTestResults(settings: AardSettings): AardTestResults {
@ -65,15 +71,35 @@ export function initAardTestResults(settings: AardSettings): AardTestResults {
bottomCandidate: 0, bottomCandidate: 0,
bottomCandidateDistance: 0, bottomCandidateDistance: 0,
bottomCandidateQuality: 0, bottomCandidateQuality: 0,
topRowsDifferenceMatrix: [0, 0, 0],
bottomRowsDifferenceMatrix: [0, 0, 0],
}, },
aspectRatioUncertain: false, aspectRatioUncertain: false,
topRowUncertain: false,
bottomRowUncertain: false,
aspectRatioUpdated: false, aspectRatioUpdated: false,
activeAspectRatio: 0,
letterboxWidth: 0, letterboxWidth: 0,
letterboxOffset: 0, letterboxOffset: 0,
logoDetected: [false, false, false, false] logoDetected: [false, false, false, false]
} }
} }
export function resetGuardLine(results: AardTestResults) {
results.guardLine.top = -1;
results.guardLine.bottom = -1;
results.imageLine.invalidated = false;
results.guardLine.invalidated = false;
results.guardLine.cornerViolated[0] = false;
results.guardLine.cornerViolated[1] = false;
results.guardLine.cornerViolated[2] = false;
results.guardLine.cornerViolated[3] = false;
results.guardLine.cornerPixelsViolated[0] = 0;
results.guardLine.cornerPixelsViolated[1] = 0;
results.guardLine.cornerPixelsViolated[2] = 0;
results.guardLine.cornerPixelsViolated[3] = 0;
}
export function resetAardTestResults(results: AardTestResults): void { export function resetAardTestResults(results: AardTestResults): void {
results.isFinished = false; results.isFinished = false;
results.lastStage = 0; results.lastStage = 0;
@ -88,7 +114,10 @@ export function resetAardTestResults(results: AardTestResults): void {
results.guardLine.cornerPixelsViolated[1] = 0; results.guardLine.cornerPixelsViolated[1] = 0;
results.guardLine.cornerPixelsViolated[2] = 0; results.guardLine.cornerPixelsViolated[2] = 0;
results.guardLine.cornerPixelsViolated[3] = 0; results.guardLine.cornerPixelsViolated[3] = 0;
results.letterboxWidth = 0; // results.letterboxWidth = 0;
results.letterboxOffset = 0; // results.letterboxOffset = 0;
results.aspectRatioUpdated = false; results.aspectRatioUpdated = false;
results.aspectRatioUncertainReason = null;
results.topRowUncertain = false;
results.bottomRowUncertain = false;
} }

View File

@ -1,9 +1,11 @@
export interface AardTimers { export interface AardTimers {
nextFrameCheckTime: number; nextFrameCheckTime: number;
reducedPollingNextCheckTime: number;
} }
export function initAardTimers(): AardTimers { export function initAardTimers(): AardTimers {
return { return {
nextFrameCheckTime: 0 nextFrameCheckTime: 0,
reducedPollingNextCheckTime: 0,
}; };
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,129 +0,0 @@
class DebugCanvas {
constructor(ardConf){
this.conf = ardConf;
this.targetWidth = 1280;
this.targetHeight = 720;
this.targetOffsetTop = 1080;
this.targetOffsetLeft = 100;
}
init(canvasSize, canvasPosition, targetCanvasSize) {
console.log("initiating DebugCanvas")
var body = document.getElementsByTagName('body')[0];
if(!canvasPosition){
canvasPosition = {
top: 1200,
left: 800
}
}
if(!this.canvas){
this.canvas = document.createElement("canvas");
body.appendChild(this.canvas);
}
if(targetCanvasSize){
this.targetWidth = targetCanvasSize.width;
this.targetHeight = targetCanvasSize.height;
}
this.canvas.style.position = "absolute";
this.canvas.style.left = `${canvasPosition.left}px`;
this.canvas.style.top = `${canvasPosition.top}px`;
this.canvas.style.zIndex = 10002;
// this.canvas.id = "uw_debug_canvas";
this.context = this.canvas.getContext("2d");
this.canvas.width = canvasSize.width;
this.canvas.height = canvasSize.height;
this.calculateCanvasZoom();
console.log("debug canvas is:", this.canvas, "context:", this.context)
}
calculateCanvasZoom(){
var canvasZoom = this.targetWidth / this.canvas.width;
var translateX = (this.canvas.width - this.targetWidth)/2;
var translateY = (this.canvas.height - this.targetHeight)/2;
this.canvas.style.transform = `scale(${canvasZoom},${canvasZoom}) translateX(${translateX}px) translateY(${translateY}px)`;
}
destroy(){
if(this.canvas)
this.canvas.remove();
}
setBuffer(buffer) {
// this.imageBuffer = buffer.splice(0);
this.imageBuffer = new Uint8ClampedArray(buffer);
}
trace(arrayIndex, colorClass) {
this.imageBuffer[arrayIndex ] = colorClass.colorRgb[0];
this.imageBuffer[arrayIndex+1] = colorClass.colorRgb[1];
this.imageBuffer[arrayIndex+2] = colorClass.colorRgb[2];
}
update() {
var start = performance.now();
try{
if(this.context && this.imageBuffer instanceof Uint8ClampedArray){
try{
var idata = new ImageData(this.imageBuffer, this.canvas.width, this.canvas.height);
} catch (ee) {
console.log("[DebugCanvas.js::update] can't create image data. Trying to correct canvas size. Error was:", ee);
this.canvas.width = this.conf.canvas.width;
this.canvas.height = this.conf.canvas.height;
this.calculateCanvasZoom();
// this.context = this.canvas.getContext("2d");
var idata = new ImageData(this.imageBuffer, this.canvas.width, this.canvas.height);
}
this.putImageData(this.context, idata, 0, 0, 0, 0, this.canvas.width, this.canvas.height);
}
} catch(e) {
console.log("[DebugCanvas.js::update] updating canvas failed.", e);
}
console.log("[DebugCanvas.js::update] update took", (performance.now() - start), "ms.");
}
putImageData(ctx, imageData, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight) {
var data = imageData.data;
var height = imageData.height;
var width = imageData.width;
dirtyX = dirtyX || 0;
dirtyY = dirtyY || 0;
dirtyWidth = dirtyWidth !== undefined? dirtyWidth: width;
dirtyHeight = dirtyHeight !== undefined? dirtyHeight: height;
var limitBottom = Math.min(dirtyHeight, height);
var limitRight = Math.min(dirtyWidth, width);
for (var y = dirtyY; y < limitBottom; y++) {
for (var x = dirtyX; x < limitRight; x++) {
var pos = y * width + x;
ctx.fillStyle = 'rgba(' + data[pos*4+0]
+ ',' + data[pos*4+1]
+ ',' + data[pos*4+2]
+ ',' + (data[pos*4+3]/255) + ')';
ctx.fillRect(x + dx, y + dy, 1, 1);
}
}
}
}
DebugCanvasClasses = {
VIOLATION: {color: '#ff0000', colorRgb: [255, 0, 0], text: 'violation (general)'},
WARN: {color: '#d0d039', colorRgb: [208, 208, 57], text: 'lesser violation (general)'},
GUARDLINE_BLACKBAR: {color: '#3333FF', colorRgb: [51, 51, 255], text: 'guardline/blackbar (expected value)'},
GUARDLINE_IMAGE: {color: '#000088', colorRgb: [0, 0, 136], text: 'guardline/image (expected value)'},
EDGEDETECT_ONBLACK: {color: '#444444', colorRgb: [68, 68, 68], text: 'edge detect - perpendicular (to edge)'},
EDGEDETECT_CANDIDATE: {color: '#FFFFFF', colorRgb: [255, 255, 255], text: 'edge detect - edge candidate'},
EDGEDETECT_CANDIDATE_SECONDARY: {color: '#OOOOOO', colorRgb: [0, 0, 0], text: 'edge detect - edge candidate, but for when candidate is really bright'},
EDGEDETECT_BLACKBAR: {color: '#07ac93', colorRgb: [7, 172, 147], text: 'edge detect - parallel, black test'},
EDGEDETECT_IMAGE: {color: '#046c5c', colorRgb: [4, 108, 92], text: 'edge detect - parallel, image test'}
}

View File

@ -1,291 +0,0 @@
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){
this.blackbar = {top: undefined, bottom: undefined};
this.imageBar = {top: undefined, bottom: undefined};
this.aard = ardConf;
this.settings = ardConf.settings;
}
reset() {
this.blackbar = {top: undefined, bottom: undefined};
this.imageBar = {top: undefined, bottom: undefined};
}
setBlackbar(bbconf){
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.aard.glCanvas.height ){
throw {error: "INVALID_SETTINGS_IN_GUARDLINE", bbTop, bbBottom}
}
this.blackbar = {
top: bbTop,
bottom: bbBottom
}
this.imageBar = {
top: bbconf.top + 1 + this.settings.active.arDetect.guardLine.edgeTolerancePx,
bottom: bbconf.bottom - 1 - this.settings.active.arDetect.guardLine.edgeTolerancePx
}
}
check(image){
// calculate once and save object-instance-wide
this.blackbarThreshold = this.aard.blackLevel + this.settings.active.arDetect.blackbar.threshold;
this.imageThreshold = this.blackbarThreshold + this.settings.active.arDetect.blackbar.imageThreshold;
// actual checks
let guardLineResult = this.guardLineCheck(image);
// blackbar violation detected. We don't even need to check for presence of image
// as aspect ratio just decreased
if(! guardLineResult.success) {
return {
blackbarFail: true,
offenders: guardLineResult.offenders,
imageFail: false
}
}
let imageCheckResult = this.imageCheck(image);
return {
blackbarFail: false,
imageFail: ! imageCheckResult.success
}
}
// don't use methods below this line outside this class
guardLineCheck(image){
// this test tests for whether we crop too aggressively
// if this test is passed, then aspect ratio probably didn't change from wider to narrower. However, further
// checks are needed to determine whether aspect ratio got wider.
// if this test fails, it returns a list of offending points.
// if the upper edge is null, then edge hasn't been detected before. This test is pointless, therefore it
// should succeed by default. Also need to check bottom, for cases where only one edge is known
if (!this.blackbar.top || !this.blackbar.bottom) {
return { success: true };
}
let offset = (this.aard.glCanvas.width * this.settings.active.arDetect.guardLine.ignoreEdgeMargin) << 2;
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;
edge_upper = this.blackbar.top;
edge_lower = this.blackbar.bottom;
let rowStart, rowEnd;
// <<<=======| checking upper row |========>>>
rowStart = ((edge_upper * this.aard.glCanvas.width) << 2) + offset;
rowEnd = rowStart + ( this.aard.glCanvas.width << 2 ) - (offset * 2);
if (Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine) {
// 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.aard.glCanvas.width) << 2) + offset;
rowEnd = rowStart + ( this.aard.glCanvas.width << 2 ) - (offset * 2);
if (Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine) {
// offenderCount = this._gl_debugRowCheck(image, rowStart, rowEnd, offenders, offenderCount);
} else {
offenderCount = this._gl_rowCheck(image, rowStart, rowEnd, offenders, offenderCount);
}
// če nismo našli nobenih prekrškarjev, vrnemo uspeh. Drugače vrnemo seznam prekrškarjev
// vrnemo tabelo, ki vsebuje sredinsko točko vsakega prekrškarja (x + width*0.5)
//
// if we haven't found any offenders, we return success. Else we return list of offenders
// we return array of middle points of offenders (x + (width * 0.5) for every offender)
if(offenderCount == -1){
return {success: true};
}
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): ImageCheckResult {
if(!this.imageBar.top || !this.imageBar.bottom)
return { success: false };
let offset = (this.aard.glCanvas.width * this.settings.active.arDetect.guardLine.ignoreEdgeMargin) << 2;
// TODO: implement logo check.
let edge_upper = this.imageBar.top;
let edge_lower = this.imageBar.bottom;
// 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)
let successThreshold = (this.aard.glCanvas.width * this.settings.active.arDetect.guardLine.imageTestThreshold);
let rowStart, rowEnd;
// <<<=======| checking upper row |========>>>
rowStart = ((edge_upper * this.aard.glCanvas.width) << 2) + offset;
rowEnd = rowStart + ( this.aard.glCanvas.width << 2 ) - (offset * 2);
let res = false;
if(Debug.debugCanvas.enabled && Debug.debugCanvas.guardLine){
// res = this._ti_debugCheckRow(image, rowStart, rowEnd, successThreshold);
} else {
res = this._ti_checkRow(image, rowStart, rowEnd,successThreshold);
}
if (res) {
return {success: true};
}
// <<<=======| checking lower row |========>>>
rowStart = ((edge_lower * this.aard.glCanvas.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);
} else {
res = this._ti_checkRow(image, rowStart, rowEnd,successThreshold);
}
return {success: res};
}
// pomožne metode
// helper methods
_gl_rowCheck(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){
if(firstOffender < 0){
firstOffender = (i - rowStart) >> 2;
offenderCount++;
offenders.push({x: firstOffender, width: 1});
}
else{
offenders[offenderCount].width++
}
}
else{
// is that a black pixel again? Let's reset the 'first offender'
firstOffender = -1;
}
}
return offenderCount;
}
// _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.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;
// }
_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;
}
}
}
return false;
}
// _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;
// }
}
export default GuardLine;

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -137,7 +137,7 @@ class CommsClient {
try { try {
return this.port.postMessage(message); return this.port.postMessage(message);
} catch (e) { } catch (e) {
console.log('chrome is shit, lets try to bruteforce ...', e); // console.log('chrome is shit, lets try to bruteforce ...', e);
const port = chrome.runtime.connect(null, {name: this.name}); const port = chrome.runtime.connect(null, {name: this.name});
port.onMessage.addListener(this._listener); port.onMessage.addListener(this._listener);
return port.postMessage(message); return port.postMessage(message);
@ -152,7 +152,7 @@ class CommsClient {
* @param receivedMessage * @param receivedMessage
*/ */
private processReceivedMessage(receivedMessage){ private processReceivedMessage(receivedMessage){
console.log('message popped out of the comms', receivedMessage, 'event bus:', this.eventBus); // console.log('message popped out of the comms', receivedMessage, 'event bus:', this.eventBus);
// when sending between frames, message will be enriched with two new properties // when sending between frames, message will be enriched with two new properties
const {_sourceFrame, _sourcePort, ...message} = receivedMessage; const {_sourceFrame, _sourcePort, ...message} = receivedMessage;

View File

@ -148,14 +148,7 @@ class UI {
initMessaging() { initMessaging() {
// subscribe to events coming back to us. Unsubscribe if iframe vanishes. // subscribe to events coming back to us. Unsubscribe if iframe vanishes.
const messageHandlerFn = (message) => { window.addEventListener('message', this.messageHandlerFn);
if (!this.uiIframe?.contentWindow) {
window.removeEventListener('message', messageHandlerFn);
return;
}
this.handleMessage(message);
}
window.addEventListener('message', messageHandlerFn);
/* set up event bus tunnel from content script to UI necessary if we want to receive /* set up event bus tunnel from content script to UI necessary if we want to receive
@ -163,17 +156,14 @@ class UI {
* necessary for UI display in the popup. * necessary for UI display in the popup.
*/ */
this.eventBus.subscribe( this.eventBus.subscribeMulti(
'uw-config-broadcast',
{ {
'uw-config-broadcast': {
function: (config, routingData) => { function: (config, routingData) => {
this.sendToIframe('uw-config-broadcast', config, routingData); this.sendToIframe('uw-config-broadcast', config, routingData);
} }
} },
); 'uw-set-ui-state': {
this.eventBus.subscribe(
'uw-set-ui-state',
{
function: (config, routingData) => { function: (config, routingData) => {
if (config.globalUiVisible !== undefined) { if (config.globalUiVisible !== undefined) {
if (this.isGlobal) { if (this.isGlobal) {
@ -184,10 +174,8 @@ class UI {
} }
this.sendToIframe('uw-set-ui-state', {...config, isGlobal: this.isGlobal}, routingData); this.sendToIframe('uw-set-ui-state', {...config, isGlobal: this.isGlobal}, routingData);
} }
} },
) 'uw-restore-ui-state': {
this.eventBus.subscribe(
'uw-restore-ui-state', {
function: (config, routingData) => { function: (config, routingData) => {
if (!this.isGlobal) { if (!this.isGlobal) {
this.setUiVisibility(true); this.setUiVisibility(true);
@ -195,7 +183,17 @@ class UI {
} }
} }
} }
) },
this
);
}
messageHandlerFn = (message) => {
if (!this.uiIframe?.contentWindow) {
window.removeEventListener('message', this.messageHandlerFn);
return;
}
this.handleMessage(message);
} }
setUiVisibility(visible) { setUiVisibility(visible) {
@ -360,6 +358,8 @@ class UI {
} }
destroy() { destroy() {
window.removeEventListener('message', this.messageHandlerFn);
this.eventBus.unsubscribeAll(this);
// this.comms?.destroy(); // this.comms?.destroy();
this.uiIframe?.remove(); this.uiIframe?.remove();
this.element?.remove(); this.element?.remove();

View File

@ -36,18 +36,24 @@ export default class IframeManager {
if (this.isIframe) { if (this.isIframe) {
window.addEventListener('beforeunload', this.destroy); window.addEventListener('beforeunload', this.destroy);
this.eventBus.subscribe( this.eventBus.subscribe(
'uw-frame-ping', {function: (cmd, context) => this.handleIframePing(context)} 'uw-frame-ping',
{
source: this,
function: (cmd, context) => this.handleIframePing(context)
}
); );
this.eventBus.send( this.eventBus.send(
'uw-frame-register', {host: window.location.hostname}, {comms: {forwardTo: 'all-frames'}} 'uw-frame-register', {host: window.location.hostname}, {comms: {forwardTo: 'all-frames'}}
); );
} else { } else {
this.eventBus.subscribe( this.eventBus.subscribeMulti(
'uw-frame-register', {function: (data, context) => this.handleIframeRegister(data, context)} {
); 'uw-frame-register': {function: (data, context) => this.handleIframeRegister(data, context)},
this.eventBus.subscribe( 'uw-frame-destroyed': {function: (cmd, context) => this.handleFrameDestroyed(context)}
'uw-frame-destroyed', {function: (cmd, context) => this.handleFrameDestroyed(context)} },
this
); );
// register all frames to re-register themselves // register all frames to re-register themselves
@ -69,6 +75,7 @@ export default class IframeManager {
} }
}, },
) )
this.eventBus.unsubscribeAll(this);
} }
private handleIframePing(context) { private handleIframePing(context) {

View File

@ -112,7 +112,7 @@ class PageInfo {
} }
destroy() { destroy() {
this.logger.log('info', ['debug', 'init'], "[PageInfo::destroy] destroying all videos!") // this.logger.log('info', ['debug', 'init'], "[PageInfo::destroy] destroying all videos!")
if(this.rescanTimer){ if(this.rescanTimer){
clearTimeout(this.rescanTimer); clearTimeout(this.rescanTimer);
} }
@ -179,17 +179,23 @@ class PageInfo {
} }
} }
getVideos(host) { getVideos(): HTMLVideoElement[] {
const videoQs = this.siteSettings.getCustomDOMQuerySelector('video'); const videoQs = this.siteSettings.getCustomDOMQuerySelector('video');
if (videoQs){ let videos: HTMLVideoElement[] = [];
const videos = document.querySelectorAll(videoQs) as NodeListOf<HTMLVideoElement>;
if (videoQs){
videos = Array.from(document.querySelectorAll(videoQs) as NodeListOf<HTMLVideoElement> ?? []);
} else{
videos = Array.from(document.getElementsByTagName('video') ?? []);
}
// filter out videos that aren't big enough
videos = videos.filter(
(v: HTMLVideoElement) => v.clientHeight > 720 && v.clientWidth > 1208
);
if (videos.length) {
return videos; return videos;
} }
}
return document.getElementsByTagName('video');
}
hasVideo() { hasVideo() {
return this.readOnly ? this.hasVideos : this.videos.length; return this.readOnly ? this.hasVideos : this.videos.length;
@ -206,6 +212,7 @@ class PageInfo {
// is there any video data objects that had their HTML elements removed but not yet // is there any video data objects that had their HTML elements removed but not yet
// destroyed? We clean that up here. // destroyed? We clean that up here.
const orphans = this.videos.filter(x => !document.body.contains(x.element)); const orphans = this.videos.filter(x => !document.body.contains(x.element));
for (const orphan of orphans) { for (const orphan of orphans) {
orphan.videoData.destroy(); orphan.videoData.destroy();
} }
@ -215,7 +222,7 @@ class PageInfo {
// add new videos // add new videos
try{ try{
let vids = this.getVideos(window.location.hostname); let vids = this.getVideos();
if(!vids || vids.length == 0){ if(!vids || vids.length == 0){
this.hasVideos = false; this.hasVideos = false;

View File

@ -80,6 +80,7 @@ class PlayerData {
halted: boolean = true; halted: boolean = true;
isFullscreen: boolean = !!document.fullscreenElement; isFullscreen: boolean = !!document.fullscreenElement;
isTheaterMode: boolean = false; // note: fullscreen mode will count as theaterMode if player was in theater mode before fs switch. This is desired, so far. isTheaterMode: boolean = false; // note: fullscreen mode will count as theaterMode if player was in theater mode before fs switch. This is desired, so far.
isTooSmall: boolean = true;
//#region misc stuff //#region misc stuff
extensionMode: any; extensionMode: any;
@ -87,6 +88,8 @@ class PlayerData {
private playerIdElement: any; private playerIdElement: any;
private observer: ResizeObserver; private observer: ResizeObserver;
private trackChangesTimeout: any;
private ui: UI; private ui: UI;
elementStack: any[] = []; elementStack: any[] = [];
@ -99,7 +102,7 @@ class PlayerData {
}], }],
'get-player-dimensions': [{ 'get-player-dimensions': [{
function: () => { function: () => {
this.eventBus.send('uw-config-broadcast', { this.eventBus.send('—————————————————————————— uw-config-broadcast', {
type: 'player-dimensions', type: 'player-dimensions',
data: this.dimensions data: this.dimensions
}); });
@ -157,20 +160,12 @@ class PlayerData {
// do the rest // do the rest
this.invalid = false; this.invalid = false;
this.element = this.getPlayer(); this.element = this.getPlayer();
this.isTooSmall = (this.element.clientWidth < 1208 || this.element.clientHeight < 720);
this.initEventBus(); this.initEventBus();
// this.notificationService = new PlayerNotificationUi(this.element, this.settings, this.eventBus); // we defer UI creation until player element is big enough
if (this.videoData.settings.active.ui?.inPlayer?.enabled) { // this happens in trackDimensionChanges!
this.ui = new UI(
'ultrawidifyUi',
{
parentElement: this.element,
eventBus: this.eventBus,
playerData: this,
uiSettings: this.videoData.settings.active.ui
}
);
}
this.dimensions = undefined; this.dimensions = undefined;
this.overlayNode = undefined; this.overlayNode = undefined;
@ -233,10 +228,46 @@ class PlayerData {
destroy() { destroy() {
document.removeEventListener('fullscreenchange', this.dimensionChangeListener); document.removeEventListener('fullscreenchange', this.dimensionChangeListener);
this.stopChangeDetection(); this.stopChangeDetection();
this.ui?.destroy();
this.notificationService?.destroy(); this.notificationService?.destroy();
} }
//#endregion //#endregion
deferredUiInitialization(playerDimensions) {
if (this.ui || ! this.videoData.settings.active.ui?.inPlayer?.enabled) {
return;
}
if (
this.isFullscreen
|| (
playerDimensions.width > 1208
&& playerDimensions.height > 720
)
) {
this.ui = new UI(
'ultrawidifyUi',
{
parentElement: this.element,
eventBus: this.eventBus,
playerData: this,
uiSettings: this.videoData.settings.active.ui
}
);
if (this.runLevel < RunLevel.UIOnly) {
this.ui?.disable();
}
if (this.runLevel >= RunLevel.UIOnly) {
this.ui?.enable();
this.startChangeDetection();
}
if (this.runLevel >= RunLevel.CustomCSSActive) {
this.element.classList.add(this.playerCssClass);
}
}
}
/** /**
* Sets extension runLevel and sets or unsets appropriate css classes as necessary * Sets extension runLevel and sets or unsets appropriate css classes as necessary
* @param runLevel * @param runLevel
@ -253,11 +284,11 @@ class PlayerData {
this.element.classList.remove(this.playerCssClass); this.element.classList.remove(this.playerCssClass);
} }
if (runLevel < RunLevel.UIOnly) { if (runLevel < RunLevel.UIOnly) {
this.ui.disable(); this.ui?.disable();
} }
} else { } else {
if (runLevel >= RunLevel.UIOnly) { if (runLevel >= RunLevel.UIOnly) {
this.ui.enable(); this.ui?.enable();
this.startChangeDetection(); this.startChangeDetection();
} }
if (runLevel >= RunLevel.CustomCSSActive) { if (runLevel >= RunLevel.CustomCSSActive) {
@ -313,12 +344,20 @@ class PlayerData {
this.detectTheaterMode(); this.detectTheaterMode();
} }
// defer creating UI
this.deferredUiInitialization(currentPlayerDimensions);
// if dimensions of the player box are the same as the last known // if dimensions of the player box are the same as the last known
// dimensions, we don't have to do anything // dimensions, we don't have to do anything ... in theory. In practice,
// sometimes restore-ar doesn't appear to register the first time, and
// this function doesn't really run often enough to warrant finding a
// real, optimized fix.
if ( if (
this.dimensions?.width == currentPlayerDimensions.width this.dimensions?.width == currentPlayerDimensions.width
&& this.dimensions?.height == currentPlayerDimensions.height && this.dimensions?.height == currentPlayerDimensions.height
) { ) {
this.eventBus.send('restore-ar', null);
this.eventBus.send('delayed-restore-ar', {delay: 500});
this.dimensions = currentPlayerDimensions; this.dimensions = currentPlayerDimensions;
return; return;
} }
@ -326,7 +365,7 @@ class PlayerData {
// in every other case, we need to check if the player is still // in every other case, we need to check if the player is still
// big enough to warrant our extension running. // big enough to warrant our extension running.
this.handleSizeConstraints(currentPlayerDimensions); this.handleSizeConstraints(currentPlayerDimensions);
this.handleDimensionChanges(currentPlayerDimensions, this.dimensions); // this.handleDimensionChanges(currentPlayerDimensions, this.dimensions);
// Save current dimensions to avoid triggering this function pointlessly // Save current dimensions to avoid triggering this function pointlessly
this.dimensions = currentPlayerDimensions; this.dimensions = currentPlayerDimensions;
@ -338,19 +377,15 @@ class PlayerData {
* @param currentPlayerDimensions * @param currentPlayerDimensions
*/ */
private handleSizeConstraints(currentPlayerDimensions: PlayerDimensions) { private handleSizeConstraints(currentPlayerDimensions: PlayerDimensions) {
console.log('handling resize constraints');
// Check if extension is allowed to run in current combination of theater + full screen // Check if extension is allowed to run in current combination of theater + full screen
const canEnable = this.siteSettings.isEnabledForEnvironment(this.isFullscreen, this.isTheaterMode) === ExtensionMode.Enabled; const canEnable = this.siteSettings.isEnabledForEnvironment(this.isFullscreen, this.isTheaterMode) === ExtensionMode.Enabled;
if (this.runLevel === RunLevel.Off && canEnable) { if (this.runLevel === RunLevel.Off && canEnable) {
console.log('runLevel: off -> [anything]');
this.eventBus.send('restore-ar', null); this.eventBus.send('restore-ar', null);
// must be called after // must be called after
this.handleDimensionChanges(currentPlayerDimensions, this.dimensions); this.handleDimensionChanges(currentPlayerDimensions, this.dimensions);
} else if (!canEnable && this.runLevel !== RunLevel.Off) { } else if (!canEnable && this.runLevel !== RunLevel.Off) {
// must be called before // must be called before
console.log('runLevel: [anything] -> off');
this.handleDimensionChanges(currentPlayerDimensions, this.dimensions); this.handleDimensionChanges(currentPlayerDimensions, this.dimensions);
this.setRunLevel(RunLevel.Off); this.setRunLevel(RunLevel.Off);
} }
@ -358,7 +393,6 @@ class PlayerData {
private handleDimensionChanges(newDimensions: PlayerDimensions, oldDimensions: PlayerDimensions) { private handleDimensionChanges(newDimensions: PlayerDimensions, oldDimensions: PlayerDimensions) {
console.log('handling dimension changes\n\nold dimensions:', oldDimensions, '\nnew dimensions:', newDimensions, '\n\nis enabled:', this.enabled, this.runLevel, RunLevel);
if (this.runLevel === RunLevel.Off ) { if (this.runLevel === RunLevel.Off ) {
this.logger.log('info', 'debug', "[PlayerDetect] player size changed, but PlayerDetect is in disabled state. The player element is probably too small."); this.logger.log('info', 'debug', "[PlayerDetect] player size changed, but PlayerDetect is in disabled state. The player element is probably too small.");
return; return;
@ -374,7 +408,6 @@ class PlayerData {
|| newDimensions?.height != oldDimensions?.height || newDimensions?.height != oldDimensions?.height
|| newDimensions?.fullscreen != oldDimensions?.fullscreen || newDimensions?.fullscreen != oldDimensions?.fullscreen
){ ){
console.log('dimensions changed + we are enabled. Sending restore-ar ...');
// If player size changes, we restore aspect ratio // If player size changes, we restore aspect ratio
this.eventBus.send('restore-ar', null); this.eventBus.send('restore-ar', null);
this.eventBus.send('delayed-restore-ar', {delay: 500}); this.eventBus.send('delayed-restore-ar', {delay: 500});
@ -383,6 +416,9 @@ class PlayerData {
type: 'player-dimensions', type: 'player-dimensions',
data: newDimensions data: newDimensions
}); });
this.isTooSmall = !newDimensions.fullscreen && (newDimensions.width < 1208 || newDimensions.height < 720);
} }
} }
@ -515,20 +551,15 @@ class PlayerData {
} }
// if mode is given, we follow the preference // if mode is given, we follow the preference
console.log('we prefer manual mode:', this.siteSettings, this.siteSettings.data.currentDOMConfig?.elements?.player);
if (this.siteSettings.data.currentDOMConfig?.elements?.player?.manual && this.siteSettings.data.currentDOMConfig?.elements?.player?.mode) { if (this.siteSettings.data.currentDOMConfig?.elements?.player?.manual && this.siteSettings.data.currentDOMConfig?.elements?.player?.mode) {
console.log('we prefer manual mode:', this.siteSettings.data.currentDOMConfig?.elements?.player?.mode);
if (this.siteSettings.data.currentDOMConfig?.elements?.player?.mode === 'qs') { if (this.siteSettings.data.currentDOMConfig?.elements?.player?.mode === 'qs') {
playerCandidate = this.getPlayerQs(playerQs, elementStack, videoWidth, videoHeight); playerCandidate = this.getPlayerQs(playerQs, elementStack, videoWidth, videoHeight);
console.log('got qs player candidate');
} else { } else {
playerCandidate = elementStack[playerIndex]; playerCandidate = elementStack[playerIndex];
playerCandidate.heuristics['manualElementByParentIndex'] = true; playerCandidate.heuristics['manualElementByParentIndex'] = true;
console.log('got index player candidate')
} }
} else { } else {
console.log('no preference.')
// try to figure it out based on what we have, with playerQs taking priority // try to figure it out based on what we have, with playerQs taking priority
if (playerQs) { if (playerQs) {
playerCandidate = this.getPlayerQs(playerQs, elementStack, videoWidth, videoHeight); playerCandidate = this.getPlayerQs(playerQs, elementStack, videoWidth, videoHeight);
@ -702,10 +733,10 @@ class PlayerData {
* Lists elements between video and DOM root for display in player selector (UI) * Lists elements between video and DOM root for display in player selector (UI)
*/ */
private handlePlayerTreeRequest() { private handlePlayerTreeRequest() {
console.log('aya')
// this populates this.elementStack fully // this populates this.elementStack fully
this.getPlayer({verbose: true}); this.getPlayer({verbose: true});
console.log('tree:', JSON.parse(JSON.stringify(this.elementStack))); console.log('tree:', JSON.parse(JSON.stringify(this.elementStack)));
console.log('————————————————————— handling player tree request!')
this.eventBus.send('uw-config-broadcast', {type: 'player-tree', config: JSON.parse(JSON.stringify(this.elementStack))}); this.eventBus.send('uw-config-broadcast', {type: 'player-tree', config: JSON.parse(JSON.stringify(this.elementStack))});
} }

View File

@ -88,15 +88,15 @@ class VideoData {
} }
private eventBusCommands = { private eventBusCommands = {
'get-drm-status': [{ 'get-drm-status': {
function: () => { function: () => {
this.hasDrm = hasDrm(this.video); this.hasDrm = hasDrm(this.video);
this.eventBus.send('uw-config-broadcast', {type: 'drm-status', hasDrm: this.hasDrm}); this.eventBus.send('uw-config-broadcast', {type: 'drm-status', hasDrm: this.hasDrm});
} }
}], },
'set-run-level': [{ 'set-run-level': {
function: (runLevel: RunLevel) => this.setRunLevel(runLevel) function: (runLevel: RunLevel) => this.setRunLevel(runLevel)
}] }
} }
/** /**
@ -128,19 +128,18 @@ class VideoData {
height: this.video.offsetHeight, height: this.video.offsetHeight,
}; };
if (!pageInfo.eventBus) {
this.eventBus = new EventBus(); this.eventBus = new EventBus();
} else {
this.eventBus = pageInfo.eventBus;
}
this.extensionStatus = new ExtensionStatus(siteSettings, pageInfo.eventBus, pageInfo.fsStatus); this.extensionStatus = new ExtensionStatus(siteSettings, pageInfo.eventBus, pageInfo.fsStatus);
if (pageInfo.eventBus) { this.eventBus.subscribeMulti(
this.eventBus.setUpstreamBus(pageInfo.eventBus); this.eventBusCommands,
this
for (const action in this.eventBusCommands) { );
for (const command of this.eventBusCommands[action]) {
this.eventBus.subscribe(action, command);
}
}
}
this.setupEventListeners(); this.setupEventListeners();
} }
@ -243,10 +242,8 @@ class VideoData {
} }
this.resizer = new Resizer(this); this.resizer = new Resizer(this);
console.log('before init aard');
try { try {
this.aard = new Aard(this); // this starts Ar detection. needs optional parameter that prevents ArDetector from starting this.aard = new Aard(this); // this starts Ar detection. needs optional parameter that prevents ArDetector from starting
console.log('after init aard');
} catch (e) { } catch (e) {
console.error('Failed to start Aard!', e); console.error('Failed to start Aard!', e);
} }
@ -270,7 +267,6 @@ class VideoData {
* Must be triggered on first action. TODO * Must be triggered on first action. TODO
*/ */
preparePage() { preparePage() {
console.log('preparing page ...')
this.injectBaseCss(); this.injectBaseCss();
this.pageInfo.initMouseActionHandler(this); this.pageInfo.initMouseActionHandler(this);
@ -365,7 +361,7 @@ class VideoData {
this.eventBus.send('set-run-level', RunLevel.Off); this.eventBus.send('set-run-level', RunLevel.Off);
this.destroyed = true; this.destroyed = true;
this.eventBus?.unsetUpstreamBus(); this.eventBus.unsubscribeAll(this);
try { try {
this.aard.stop(); this.aard.stop();
@ -389,16 +385,12 @@ class VideoData {
setRunLevel(runLevel: RunLevel, options?: {fromPlayer?: boolean}) { setRunLevel(runLevel: RunLevel, options?: {fromPlayer?: boolean}) {
console.log('setting new runlevel for videodata. current:', this.runLevel, 'new', runLevel)
if (this.runLevel === runLevel) { if (this.runLevel === runLevel) {
return; // also no need to propagate to the player return; // also no need to propagate to the player
} }
console.log('setting run level ...')
// Run level decreases towards 'off' // Run level decreases towards 'off'
if (this.runLevel > runLevel) { if (this.runLevel > runLevel) {
console.log('decreasing css.')
if (runLevel < RunLevel.CustomCSSActive) { if (runLevel < RunLevel.CustomCSSActive) {
this.video.classList.remove(this.baseCssName); this.video.classList.remove(this.baseCssName);
this.video.classList.remove(this.userCssClassName); this.video.classList.remove(this.userCssClassName);

View File

@ -63,9 +63,11 @@ class Resizer {
_lastAr: Ar = {type: AspectRatioType.Initial}; _lastAr: Ar = {type: AspectRatioType.Initial};
set lastAr(x: Ar) { set lastAr(x: Ar) {
// emit updates for UI when setting lastAr, but only if AR really changed
if (this._lastAr?.type !== x.type || this._lastAr?.ratio !== x.ratio) {
this.eventBus.send('uw-config-broadcast', {type: 'ar', config: x});
}
this._lastAr = x; this._lastAr = x;
// emit updates for UI when setting lastAr
this.eventBus.send('uw-config-broadcast', {type: 'ar', config: x})
} }
get lastAr() { get lastAr() {
return this._lastAr; return this._lastAr;

View File

@ -297,8 +297,6 @@ squeezeFactor: ${squeezeFactor}`, '\nvideo', this.conf.video);
playerAr playerAr
).xFactor; ).xFactor;
// console.info('Stretch factors before:', stretchFactors.xFactor, stretchFactors.yFactor, "max safe:", maxSafeStretchFactor, "max safe ar:", maxSafeAr);
stretchFactors.xFactor = Math.min(stretchFactors.xFactor, maxSafeStretchFactor); stretchFactors.xFactor = Math.min(stretchFactors.xFactor, maxSafeStretchFactor);
stretchFactors.yFactor = Math.min(stretchFactors.yFactor, maxSafeStretchFactor); stretchFactors.yFactor = Math.min(stretchFactors.yFactor, maxSafeStretchFactor);