Try to reduce message-passing to improve performance, make ultrawidify not run on videos smaller than 720p
This commit is contained in:
parent
162318b439
commit
cf01dd9397
@ -54,12 +54,20 @@ export type SettingsReloadFlags = true | SettingsReloadComponent;
|
||||
export interface AardSettings {
|
||||
aardType: 'webgl' | 'legacy' | 'auto';
|
||||
|
||||
earlyStopOptions: {
|
||||
stopAfterFirstDetection: boolean;
|
||||
stopAfterTimeout: boolean;
|
||||
stopTimeout: number;
|
||||
},
|
||||
|
||||
|
||||
disabledReason: string, // if automatic aspect ratio has been disabled, show reason
|
||||
allowedMisaligned: number, // top and bottom letterbox thickness can differ by this much.
|
||||
// Any more and we don't adjust ar.
|
||||
allowedArVariance: number, // amount by which old ar can differ from the new (1 = 100%)
|
||||
timers: { // autodetection frequency
|
||||
playing: number, // while playing
|
||||
playingReduced: number, // while video/player element has insufficient size
|
||||
paused: number, // while paused
|
||||
error: number, // after error
|
||||
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: {
|
||||
defaultBlack: number, // By default, pixels darker than this are considered black.
|
||||
// (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
|
||||
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
|
||||
edgeDetection: {
|
||||
|
@ -321,95 +321,107 @@ export default {
|
||||
this.handleMessage(event);
|
||||
});
|
||||
|
||||
this.eventBus.subscribe('uw-config-broadcast', {function: (data) => {
|
||||
switch (data.type) {
|
||||
case 'drm-status':
|
||||
this.statusFlags.hasDrm = data.hasDrm;
|
||||
break;
|
||||
case 'aard-error':
|
||||
this.statusFlags.aardErrors = data.aardErrors;
|
||||
break;
|
||||
case 'player-dimensions':
|
||||
this.playerDimensionsUpdate(data.data);
|
||||
break;
|
||||
}
|
||||
}});
|
||||
|
||||
this.eventBus.subscribe('uw-set-ui-state', { function: (data) => {
|
||||
if (data.globalUiVisible !== undefined) {
|
||||
if (this.isGlobal) {
|
||||
if (data.globalUiVisible) {
|
||||
this.showUwWindow();
|
||||
} else {
|
||||
this.hideUwWindow(true);
|
||||
}
|
||||
// this.showPlayerUIAfterClose = data.showPlayerUIAfterClose;
|
||||
} else {
|
||||
// non global UIs are hidden while global overlay
|
||||
// is visible and vice versa
|
||||
// this.disabled = data.globalUiVisible;
|
||||
this.saveState = {
|
||||
uwWindowVisible: this.uwWindowVisible,
|
||||
uwWindowFadeOutDisabled: this.uwWindowFadeOutDisabled,
|
||||
uwWindowFadeOut: this.uwWindowFadeOut
|
||||
};
|
||||
this.uwWindowFadeOutDisabled = false;
|
||||
this.hideUwWindow(true);
|
||||
}
|
||||
}
|
||||
}});
|
||||
|
||||
this.eventBus.subscribe(
|
||||
'uw-restore-ui-state',
|
||||
this.eventBus.subscribeMulti(
|
||||
{
|
||||
function: (data) => {
|
||||
if (this.saveState) {
|
||||
if (this.saveState.uwWindowVisible) {
|
||||
this.showUwWindow();
|
||||
'uw-config-broadcast': {
|
||||
function:
|
||||
(data) => {
|
||||
switch (data.type) {
|
||||
case 'drm-status':
|
||||
this.statusFlags.hasDrm = data.hasDrm;
|
||||
break;
|
||||
case 'aard-error':
|
||||
this.statusFlags.aardErrors = data.aardErrors;
|
||||
break;
|
||||
case 'player-dimensions':
|
||||
this.playerDimensionsUpdate(data.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
'uw-set-ui-state': {
|
||||
function: (data) => {
|
||||
if (data.globalUiVisible !== undefined) {
|
||||
if (this.isGlobal) {
|
||||
if (data.globalUiVisible) {
|
||||
this.showUwWindow();
|
||||
} else {
|
||||
this.hideUwWindow(true);
|
||||
}
|
||||
// this.showPlayerUIAfterClose = data.showPlayerUIAfterClose;
|
||||
} else {
|
||||
// non global UIs are hidden while global overlay
|
||||
// is visible and vice versa
|
||||
// this.disabled = data.globalUiVisible;
|
||||
this.saveState = {
|
||||
uwWindowVisible: this.uwWindowVisible,
|
||||
uwWindowFadeOutDisabled: this.uwWindowFadeOutDisabled,
|
||||
uwWindowFadeOut: this.uwWindowFadeOut
|
||||
};
|
||||
this.uwWindowFadeOutDisabled = false;
|
||||
this.hideUwWindow(true);
|
||||
}
|
||||
}
|
||||
this.uwWindowFadeOutDisabled = this.saveState.uwWindowFadeOutDisabled;
|
||||
this.uwWindowFadeOut = this.saveState.uwWindowFadeOut;
|
||||
}
|
||||
this.saveState = {};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.eventBus.subscribe('ui-trigger-zone-update', {
|
||||
function: (data) => {
|
||||
this.showTriggerZonePreview = data.previewZoneVisible;
|
||||
// this.;
|
||||
}
|
||||
});
|
||||
|
||||
this.eventBus.subscribe(
|
||||
'start-trigger-zone-edit',
|
||||
{
|
||||
function: () => {
|
||||
this.triggerZoneEditorVisible = true;
|
||||
this.uwWindowVisible = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.eventBus.subscribe(
|
||||
'finish-trigger-zone-edit',
|
||||
{
|
||||
function: () => {
|
||||
this.triggerZoneEditorVisible = false;
|
||||
this.showUwWindow('playerUiSettings');
|
||||
}
|
||||
}
|
||||
},
|
||||
'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.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.saveState = {};
|
||||
}
|
||||
},
|
||||
'ui-trigger-zone-update': {
|
||||
function: (data) => {
|
||||
this.showTriggerZonePreview = data.previewZoneVisible;
|
||||
// this.;
|
||||
}
|
||||
},
|
||||
'start-trigger-zone-edit': {
|
||||
function: () => {
|
||||
this.triggerZoneEditorVisible = true;
|
||||
this.uwWindowVisible = false;
|
||||
}
|
||||
},
|
||||
'finish-trigger-zone-edit': {
|
||||
function: () => {
|
||||
this.triggerZoneEditorVisible = false;
|
||||
this.showUwWindow('playerUiSettings');
|
||||
}
|
||||
},
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
this.sendToParentLowLevel('uwui-get-role', null);
|
||||
this.sendToParentLowLevel('uwui-get-theme', null);
|
||||
|
||||
console.log('player overlay created — get player dims:')
|
||||
this.sendToParentLowLevel('uw-bus-tunnel', {
|
||||
action: 'get-player-dimensions'
|
||||
});
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
this.eventBus.unsubscribeAll(this)
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Gets URL of the browser settings page (i think?)
|
||||
|
@ -148,6 +148,7 @@ export default {
|
||||
this.eventBus.subscribe(
|
||||
'set-current-site',
|
||||
{
|
||||
source: this,
|
||||
function: (config, context) => {
|
||||
if (this.site) {
|
||||
if (!this.site.host) {
|
||||
@ -169,7 +170,7 @@ export default {
|
||||
this.loadFrames(this.site);
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
this.comms = new CommsClient('popup-port', this.logger, this.eventBus);
|
||||
|
@ -204,13 +204,19 @@ export default {
|
||||
|
||||
this.eventBus.subscribe(
|
||||
'uw-show-ui',
|
||||
() => {
|
||||
if (this.inPlayer) {
|
||||
return; // show-ui is only intended for global overlay
|
||||
}
|
||||
{
|
||||
source: this,
|
||||
function: () => {
|
||||
if (this.inPlayer) {
|
||||
return; // show-ui is only intended for global overlay
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
},
|
||||
destroyed() {
|
||||
this.eventBus.unsubscribeAll(this);
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Gets URL of the browser settings page (i think?)
|
||||
|
@ -3,222 +3,252 @@
|
||||
<div class="flex flex-row flex-wrap">
|
||||
|
||||
<!-- AARD performance metrics -->
|
||||
<div class="sub-panel">
|
||||
<div>
|
||||
<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 class="sub-panel-content">
|
||||
<p>
|
||||
<b>Autodetection performance</b>
|
||||
</p>
|
||||
<p>
|
||||
Automatic aspect ratio detection is a resource-hungry feature.
|
||||
This page allows you to trade autodetection accuracy and/or frequency for
|
||||
better performance.
|
||||
</p>
|
||||
<p>
|
||||
Note that some browsers <a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/now" target="_blank">limit the accuracy of time measurements</a>, though once the bars go past the blue line those limitations are largely inconsequential.
|
||||
</p>
|
||||
<div class="performance-graph-container">
|
||||
<div class="performance-graph">
|
||||
<div class="time-budget hz144"></div>
|
||||
<div class="time-budget hz120"></div>
|
||||
<div class="time-budget hz60"></div>
|
||||
<div class="time-budget hz30"></div>
|
||||
<div class="time-budget hz24"></div>
|
||||
<div class="time-budget rest"></div>
|
||||
<div class="aard-settings-group">
|
||||
|
||||
<div class="bar-container">
|
||||
<div class="average-case">
|
||||
<div class="stats">
|
||||
<b>Average: </b>
|
||||
<span class="draw">draw (main) {{(performanceData?.imageDraw?.averageTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="draw-blackframe">blackframe {{(performanceData?.blackFrame?.averageTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="processing">
|
||||
processing {{
|
||||
Math.max(
|
||||
(performanceData?.total?.averageTime ?? 0)
|
||||
- (performanceData?.imageDraw?.averageTime ?? 0)
|
||||
- (performanceData?.blackFrame?.averageTime ?? 0),
|
||||
0
|
||||
).toFixed(1)
|
||||
}} ms
|
||||
</span>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div
|
||||
class="draw"
|
||||
:style="{'width': (performanceData?.imageDraw?.averageTime ?? 0) + '%'}"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="draw-blackframe"
|
||||
:style="{'width': (performanceData?.blackFrame?.averageTime ?? 0) + '%'}"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="processing"
|
||||
:style="{
|
||||
'width': Math.max(
|
||||
(performanceData?.total?.averageTime ?? 0)
|
||||
- (performanceData?.imageDraw?.averageTime ?? 0)
|
||||
- (performanceData?.blackFrame?.averageTime ?? 0),
|
||||
0
|
||||
) + '%'
|
||||
}"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="worst-case">
|
||||
<div class="stats">
|
||||
<b>Worst: </b>
|
||||
<span class="draw">draw (main) {{(performanceData?.imageDraw?.worstTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="draw-blackframe">blackframe {{(performanceData?.blackFrame?.worstTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="processing">
|
||||
processing {{
|
||||
Math.max(
|
||||
(performanceData?.total?.worstTime ?? 0)
|
||||
- (performanceData?.imageDraw?.worstTime ?? 0)
|
||||
- (performanceData?.blackFrame?.worstTime ?? 0),
|
||||
0
|
||||
).toFixed(1)
|
||||
}} ms
|
||||
</span>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div
|
||||
class="draw"
|
||||
:style="{'width': (performanceData?.imageDraw?.worstTime ?? 0) + '%'}"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="draw-blackframe"
|
||||
:style="{'width': (performanceData?.blackFrame?.worstTime ?? 0) + '%'}"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="processing"
|
||||
:style="{
|
||||
'width': Math.max(
|
||||
(performanceData?.total?.worstTime ?? 0)
|
||||
- (performanceData?.imageDraw?.worstTime ?? 0)
|
||||
- (performanceData?.blackFrame?.worstTime ?? 0),
|
||||
0
|
||||
) + '%'
|
||||
}"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 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>
|
||||
<b>Autodetection performance</b>
|
||||
</p>
|
||||
<p>
|
||||
Automatic aspect ratio detection is a resource-hungry feature.
|
||||
This page allows you to trade autodetection accuracy and/or frequency for
|
||||
better performance.
|
||||
</p>
|
||||
<p>
|
||||
Note that some browsers <a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/now" target="_blank">limit the accuracy of time measurements</a>, though once the bars go past the blue line those limitations are largely inconsequential.
|
||||
</p>
|
||||
<div class="performance-graph-container">
|
||||
<div class="performance-graph">
|
||||
<div class="time-budget hz144"></div>
|
||||
<div class="time-budget hz120"></div>
|
||||
<div class="time-budget hz60"></div>
|
||||
<div class="time-budget hz30"></div>
|
||||
<div class="time-budget hz24"></div>
|
||||
<div class="time-budget rest"></div>
|
||||
|
||||
<div class="average-case">
|
||||
<div class="stats">
|
||||
<b>AR change (average): </b>
|
||||
<span class="draw">draw (main) {{(performanceData?.imageDraw?.averageTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="draw-blackframe">blackframe {{(performanceData?.blackFrame?.averageTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="processing">processing {{
|
||||
(
|
||||
(performanceData?.fastLetterbox?.averageTime ?? 0)
|
||||
+ (performanceData?.edgeDetect?.averageTime ?? 0)
|
||||
).toFixed(1)
|
||||
}} ms</span>
|
||||
<div class="bar-container">
|
||||
<div class="average-case">
|
||||
<div class="stats">
|
||||
<b>Average: </b>
|
||||
<span class="draw">draw (main) {{(performanceData?.imageDraw?.averageTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="draw-blackframe">blackframe {{(performanceData?.blackFrame?.averageTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="processing">
|
||||
processing {{
|
||||
Math.max(
|
||||
(performanceData?.total?.averageTime ?? 0)
|
||||
- (performanceData?.imageDraw?.averageTime ?? 0)
|
||||
- (performanceData?.blackFrame?.averageTime ?? 0),
|
||||
0
|
||||
).toFixed(1)
|
||||
}} ms
|
||||
</span>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div
|
||||
class="draw"
|
||||
:style="{'width': (performanceData?.imageDraw?.averageTime ?? 0) + '%'}"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="draw-blackframe"
|
||||
:style="{'width': (performanceData?.blackFrame?.averageTime ?? 0) + '%'}"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="processing"
|
||||
:style="{
|
||||
'width': Math.max(
|
||||
(performanceData?.total?.averageTime ?? 0)
|
||||
- (performanceData?.imageDraw?.averageTime ?? 0)
|
||||
- (performanceData?.blackFrame?.averageTime ?? 0),
|
||||
0
|
||||
) + '%'
|
||||
}"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div
|
||||
class="draw"
|
||||
:style="{'width': (performanceData?.imageDraw?.averageTime ?? 0) + '%'}"
|
||||
>
|
||||
<div class="worst-case">
|
||||
<div class="stats">
|
||||
<b>Worst: </b>
|
||||
<span class="draw">draw (main) {{(performanceData?.imageDraw?.worstTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="draw-blackframe">blackframe {{(performanceData?.blackFrame?.worstTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="processing">
|
||||
processing {{
|
||||
Math.max(
|
||||
(performanceData?.total?.worstTime ?? 0)
|
||||
- (performanceData?.imageDraw?.worstTime ?? 0)
|
||||
- (performanceData?.blackFrame?.worstTime ?? 0),
|
||||
0
|
||||
).toFixed(1)
|
||||
}} ms
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="draw-blackframe"
|
||||
:style="{'width': (performanceData?.blackFrame?.averageTime ?? 0) + '%'}"
|
||||
>
|
||||
<div class="bar">
|
||||
<div
|
||||
class="draw"
|
||||
:style="{'width': (performanceData?.imageDraw?.worstTime ?? 0) + '%'}"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="draw-blackframe"
|
||||
:style="{'width': (performanceData?.blackFrame?.worstTime ?? 0) + '%'}"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="processing"
|
||||
:style="{
|
||||
'width': Math.max(
|
||||
(performanceData?.total?.worstTime ?? 0)
|
||||
- (performanceData?.imageDraw?.worstTime ?? 0)
|
||||
- (performanceData?.blackFrame?.worstTime ?? 0),
|
||||
0
|
||||
) + '%'
|
||||
}"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="processing"
|
||||
:style="{
|
||||
'width': (
|
||||
</div>
|
||||
|
||||
<div class="average-case">
|
||||
<div class="stats">
|
||||
<b>AR change (average): </b>
|
||||
<span class="draw">draw (main) {{(performanceData?.imageDraw?.averageTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="draw-blackframe">blackframe {{(performanceData?.blackFrame?.averageTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="processing">processing {{
|
||||
(
|
||||
(performanceData?.fastLetterbox?.averageTime ?? 0)
|
||||
+ (performanceData?.edgeDetect?.averageTime ?? 0)
|
||||
) + '%'
|
||||
}"
|
||||
>
|
||||
).toFixed(1)
|
||||
}} ms</span>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div
|
||||
class="draw"
|
||||
:style="{'width': (performanceData?.imageDraw?.averageTime ?? 0) + '%'}"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="draw-blackframe"
|
||||
:style="{'width': (performanceData?.blackFrame?.averageTime ?? 0) + '%'}"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="processing"
|
||||
:style="{
|
||||
'width': (
|
||||
(performanceData?.fastLetterbox?.averageTime ?? 0)
|
||||
+ (performanceData?.edgeDetect?.averageTime ?? 0)
|
||||
) + '%'
|
||||
}"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="worst-case">
|
||||
<div class="stats">
|
||||
<b>AR change (worst): </b>
|
||||
<span class="draw">draw (main) {{(performanceData?.imageDraw?.worstTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="draw-blackframe">blackframe {{(performanceData?.blackFrame?.worstTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="processing">processing {{
|
||||
(
|
||||
(performanceData?.fastLetterbox?.worstTime ?? 0)
|
||||
+ (performanceData?.edgeDetect?.worstTime ?? 0)
|
||||
).toFixed(1)
|
||||
}} ms</span>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div
|
||||
class="draw"
|
||||
:style="{'width': (performanceData?.imageDraw?.worstTime ?? 0) + '%'}"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="draw-blackframe"
|
||||
:style="{'width': (performanceData?.blackFrame?.worstTime ?? 0) + '%'}"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="processing"
|
||||
:style="{
|
||||
'width': (
|
||||
<div class="worst-case">
|
||||
<div class="stats">
|
||||
<b>AR change (worst): </b>
|
||||
<span class="draw">draw (main) {{(performanceData?.imageDraw?.worstTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="draw-blackframe">blackframe {{(performanceData?.blackFrame?.worstTime ?? 0).toFixed(1)}} ms</span> |
|
||||
<span class="processing">processing {{
|
||||
(
|
||||
(performanceData?.fastLetterbox?.worstTime ?? 0)
|
||||
+ (performanceData?.edgeDetect?.worstTime ?? 0)
|
||||
) + '%'
|
||||
}"
|
||||
>
|
||||
).toFixed(1)
|
||||
}} ms</span>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div
|
||||
class="draw"
|
||||
:style="{'width': (performanceData?.imageDraw?.worstTime ?? 0) + '%'}"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="draw-blackframe"
|
||||
:style="{'width': (performanceData?.blackFrame?.worstTime ?? 0) + '%'}"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="processing"
|
||||
:style="{
|
||||
'width': (
|
||||
(performanceData?.fastLetterbox?.worstTime ?? 0)
|
||||
+ (performanceData?.edgeDetect?.worstTime ?? 0)
|
||||
) + '%'
|
||||
}"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-segment">
|
||||
<h2>Basic settings</h2>
|
||||
|
||||
<div class="option">
|
||||
<div class="name">
|
||||
Autodetection frequency
|
||||
|
||||
<div class="settings-segment">
|
||||
<!-- <h2>Basic settings</h2> -->
|
||||
|
||||
<!-- <div class="field">
|
||||
<div class="label">
|
||||
Stop autodetection after first detection:
|
||||
</div>
|
||||
<div class="description">
|
||||
Shorter intervals (left side of the slider) are more responsive to changes in aspect ratio detections,
|
||||
but requires more system resources.
|
||||
<div class="">
|
||||
<input type="checkbox" v-model="settings.active.arDetect.earlyStopOptions.stopAfterFirstDetection" />
|
||||
</div>
|
||||
<div class="indent">
|
||||
<div class="flex flex-row row-padding">
|
||||
<div class="flex flex-input">
|
||||
More often <small>(~60/s)</small>
|
||||
<input type="range"
|
||||
:value="Math.log(settings.active.arDetect.timers.playing)"
|
||||
@change="setArCheckFrequency($event.target.value)"
|
||||
min="2.3"
|
||||
max="9.3"
|
||||
step="any"
|
||||
/>
|
||||
Less often <small>(~1/10s)</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="label">
|
||||
Stop detection after a period of time:
|
||||
</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)"
|
||||
@change="setArCheckFrequency($event.target.value)"
|
||||
min="2.3"
|
||||
max="9.3"
|
||||
step="0.01"
|
||||
/>
|
||||
<input
|
||||
v-model="settings.active.arDetect.timers.playing"
|
||||
class="input"
|
||||
type="text"
|
||||
>
|
||||
<div class="unit">ms</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option">
|
||||
<div class="name">
|
||||
Autodetection sensitivity
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="field">
|
||||
<div class="label">Frame extraction canvas type:</div>
|
||||
<div class="select">
|
||||
<select v-model="settings.active.arDetect.aardType">
|
||||
<option value="auto">Automatic</option>
|
||||
<option value="webgl">WebGL only</option>
|
||||
<option value="fallback">Legacy / fallback</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -227,30 +257,13 @@
|
||||
<div class="input">
|
||||
<input v-model="settings.active.arDetect.allowedMisaligned" />
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="hint">
|
||||
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
|
||||
refuse to crop it.
|
||||
refuse to crop it (% of total height).
|
||||
</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 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>
|
||||
@ -285,7 +298,16 @@ export default {
|
||||
'site'
|
||||
],
|
||||
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() {
|
||||
this.eventBus.sendToTunnel('get-aard-timing');
|
||||
@ -306,6 +328,9 @@ export default {
|
||||
async openOptionsPage() {
|
||||
BrowserDetect.runtime.openOptionsPage();
|
||||
},
|
||||
setArCheckFrequency(event) {
|
||||
this.settings.active.arDetect.timers.playing = Math.floor(Math.pow(Math.E, event));
|
||||
},
|
||||
refreshGraph() {
|
||||
this.eventBus.sendToTunnel('get-aard-timing');
|
||||
},
|
||||
@ -326,6 +351,10 @@ export default {
|
||||
<style lang="scss" scoped module>
|
||||
@import '../res-common/variables';
|
||||
|
||||
// .aard-settings-group {
|
||||
// max-width: 69rem;
|
||||
// }
|
||||
|
||||
.performance-graph-container {
|
||||
position: relative;
|
||||
|
||||
|
@ -373,11 +373,20 @@ export default({
|
||||
'isPopup'
|
||||
],
|
||||
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() {
|
||||
this.getPlayerTree();
|
||||
},
|
||||
destroyed() {
|
||||
this.eventBus.unsubscribeAll(this);
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
getPlayerTree() {
|
||||
|
@ -241,24 +241,7 @@ export default {
|
||||
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 {
|
||||
background-color: rgba(0,0,0,0.25);
|
||||
|
@ -66,49 +66,6 @@
|
||||
></StretchOptionsPanel>
|
||||
</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>
|
||||
</template>
|
||||
@ -153,11 +110,20 @@ export default {
|
||||
'site'
|
||||
],
|
||||
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() {
|
||||
this.eventBus.sendToTunnel('get-ar');
|
||||
},
|
||||
destroyed() {
|
||||
this.eventBus.unsubscribeAll(this);
|
||||
},
|
||||
components: {
|
||||
ShortcutButton,
|
||||
EditShortcutButton,
|
||||
|
@ -303,8 +303,6 @@ export default {
|
||||
return value.replaceAll(',', '.').split('.', 2).join('.').replace(/[^0-9.\-]/g, '');
|
||||
},
|
||||
setValue(key, originalValue, isTextInput) {
|
||||
console.log('trying to set value:', key, value, isTextInput);
|
||||
|
||||
let value = originalValue;
|
||||
if (isTextInput) {
|
||||
value = (+this.forceNumber(value) / 100);
|
||||
@ -312,8 +310,6 @@ export default {
|
||||
value = +this.forceNumber(value);
|
||||
}
|
||||
|
||||
console.log('rocessed value:', value);
|
||||
|
||||
if (isNaN(+value)) {
|
||||
value = 0.5;
|
||||
}
|
||||
|
@ -32,28 +32,6 @@
|
||||
<h1>Zoom:</h1>
|
||||
</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" />
|
||||
<h1>Video alignment:</h1>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row">
|
||||
<alignment-options-control-component
|
||||
:eventBus="eventBus"
|
||||
>
|
||||
</alignment-options-control-component>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
@ -81,11 +59,20 @@ export default {
|
||||
CropOptionsPanel, StretchOptionsPanel, ZoomOptionsPanel
|
||||
},
|
||||
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() {
|
||||
this.eventBus.sendToTunnel('get-ar');
|
||||
},
|
||||
destroyed() {
|
||||
this.eventBus.unsubscribeAll(this);
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
|
@ -73,18 +73,25 @@ button, .button {
|
||||
|
||||
align-items: center;
|
||||
|
||||
&.l2 {
|
||||
margin-left: 4rem;
|
||||
}
|
||||
|
||||
.label {
|
||||
flex: 0 0 25%;
|
||||
text-align: right;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
.input {
|
||||
|
||||
.input, .range-input {
|
||||
flex: 0 0 70%;
|
||||
max-width: 24rem;
|
||||
background-color: rgba($blackBg, $normalTransparentOpacity);
|
||||
border: 1px solid transparent;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.5);
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
position: relative;
|
||||
|
||||
&:active, &:focus, &:focus-within {
|
||||
border-bottom: 1px solid rgba($primary, 0.5);
|
||||
@ -97,7 +104,37 @@ button, .button {
|
||||
background-color: transparent;
|
||||
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 {
|
||||
padding-left: calc(25% + 1rem);
|
||||
font-size: 0.8rem;
|
||||
|
@ -77,6 +77,7 @@ export default class UWContent {
|
||||
this.eventBus.subscribe(
|
||||
'uw-restart',
|
||||
{
|
||||
source: this,
|
||||
function: () => this.initPhase2()
|
||||
}
|
||||
);
|
||||
@ -121,6 +122,7 @@ export default class UWContent {
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.eventBus.unsubscribeAll(this);
|
||||
if (this.pageInfo) {
|
||||
this.pageInfo.destroy();
|
||||
}
|
||||
|
@ -24,27 +24,27 @@ export default class UWServer {
|
||||
}
|
||||
|
||||
eventBusCommands = {
|
||||
'popup-set-selected-tab': [{
|
||||
'popup-set-selected-tab': {
|
||||
function: (message) => this.setSelectedTab(message.selectedMenu, message.selectedSubitem)
|
||||
}],
|
||||
'has-video': [{
|
||||
},
|
||||
'has-video': {
|
||||
function: (message, context) => this.registerVideo(context.comms.sender)
|
||||
}],
|
||||
'noVideo' : [{
|
||||
},
|
||||
'noVideo' : {
|
||||
function: (message, context) => this.unregisterVideo(context.comms.sender)
|
||||
}],
|
||||
'inject-css': [{
|
||||
},
|
||||
'inject-css': {
|
||||
function: (message, context) => this.injectCss(message.cssString, context.comms.sender)
|
||||
}],
|
||||
'eject-css': [{
|
||||
},
|
||||
'eject-css': {
|
||||
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)
|
||||
}],
|
||||
'get-current-site': [{
|
||||
},
|
||||
'get-current-site': {
|
||||
function: (message, context) => this.getCurrentSite()
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
private gcTimeout: any;
|
||||
@ -84,11 +84,8 @@ export default class UWServer {
|
||||
|
||||
this.eventBus = new EventBus({isUWServer: true});
|
||||
|
||||
for (const action in this.eventBusCommands) {
|
||||
for (const command of this.eventBusCommands[action]) {
|
||||
this.eventBus.subscribe(action, command);
|
||||
}
|
||||
}
|
||||
this.eventBus.subscribeMulti(this.eventBusCommands, this);
|
||||
|
||||
this.comms = new CommsServer(this);
|
||||
this.eventBus.setComms(this.comms);
|
||||
|
||||
|
@ -16,12 +16,20 @@ if(Debug.debug)
|
||||
const ExtensionConf: SettingsInterface = {
|
||||
arDetect: {
|
||||
aardType: 'auto',
|
||||
|
||||
earlyStopOptions: {
|
||||
stopAfterFirstDetection: false,
|
||||
stopAfterTimeout: false,
|
||||
stopTimeout: 30,
|
||||
},
|
||||
|
||||
disabledReason: "", // if automatic aspect ratio has been disabled, show reason
|
||||
allowedMisaligned: 0.05, // top and bottom letterbox thickness can differ by this much.
|
||||
// Any more and we don't adjust ar.
|
||||
allowedArVariance: 0.075, // amount by which old ar can differ from the new (1 = 100%)
|
||||
timers: { // autodetection frequency
|
||||
playing: 333, // while playing
|
||||
playingReduced: 5000, // while playing at small sizes
|
||||
paused: 3000, // while paused
|
||||
error: 3000, // after error
|
||||
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: {
|
||||
defaultBlack: 16,
|
||||
blackTolerance: 4,
|
||||
@ -101,19 +67,6 @@ const ExtensionConf: SettingsInterface = {
|
||||
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
|
||||
},
|
||||
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: {
|
||||
slopeTestWidth: 8,
|
||||
gradientTestSamples: 8,
|
||||
|
@ -4,6 +4,7 @@ import CommsServer from './comms/CommsServer';
|
||||
|
||||
export interface EventBusCommand {
|
||||
isGlobal?: boolean,
|
||||
source?: any,
|
||||
function: (commandData: any, context?: any) => void | Promise<void>
|
||||
}
|
||||
|
||||
@ -24,8 +25,6 @@ export interface EventBusContext {
|
||||
export default class EventBus {
|
||||
|
||||
private commands: { [x: string]: EventBusCommand[]} = {};
|
||||
private downstreamBuses: EventBus[] = [];
|
||||
private upstreamBus?: EventBus;
|
||||
private comms?: CommsClient | CommsServer;
|
||||
|
||||
private disableTunnel: boolean = false;
|
||||
@ -46,9 +45,6 @@ export default class EventBus {
|
||||
//#region lifecycle
|
||||
destroy() {
|
||||
this.commands = null;
|
||||
for (const bus of this.downstreamBuses) {
|
||||
bus.destroy();
|
||||
}
|
||||
this.destroyIframeTunnelling();
|
||||
}
|
||||
//#endregion
|
||||
@ -57,38 +53,6 @@ export default class EventBus {
|
||||
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) {
|
||||
if (!this.commands[commandString]) {
|
||||
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) {
|
||||
// execute commands we have subscriptions for
|
||||
|
||||
if (this.commands?.[command]) {
|
||||
for (const eventBusCommand of this.commands[command]) {
|
||||
eventBusCommand.function(commandData, context);
|
||||
@ -114,10 +101,6 @@ export default class EventBus {
|
||||
if (context?.stopPropagation) {
|
||||
return;
|
||||
}
|
||||
|
||||
// propagate commands across the bus
|
||||
this.sendUpstream(command, commandData, context);
|
||||
this.sendDownstream(command, commandData, context);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
@ -127,6 +110,7 @@ export default class EventBus {
|
||||
* @param config
|
||||
*/
|
||||
sendToTunnel(command: string, config: any) {
|
||||
console.log('sending to tunnel from eventBus ....', this)
|
||||
if (!this.disableTunnel) {
|
||||
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
|
||||
private setupIframeTunnelling() {
|
||||
// forward messages coming from iframe tunnels
|
||||
|
@ -222,9 +222,9 @@ export class Aard {
|
||||
private arid: string;
|
||||
|
||||
private eventBusCommands = {
|
||||
// 'get-aard-timing': [{
|
||||
// 'get-aard-timing': {
|
||||
// function: () => this.handlePerformanceDataRequest()
|
||||
// }]
|
||||
// }
|
||||
};
|
||||
//#endregion
|
||||
|
||||
@ -240,6 +240,8 @@ export class Aard {
|
||||
private canvasStore: AardCanvasStore;
|
||||
private testResults: AardTestResults;
|
||||
private canvasSamples: AardDetectionSample;
|
||||
|
||||
private forceFullRecheck: boolean = true;
|
||||
//#endregion
|
||||
|
||||
//#region getters
|
||||
@ -267,7 +269,8 @@ export class Aard {
|
||||
this.settings = videoData.settings;
|
||||
this.eventBus = videoData.eventBus;
|
||||
|
||||
this.initEventBus();
|
||||
this.eventBus.subscribeMulti(this.eventBusCommands, this);
|
||||
|
||||
this.arid = (Math.random()*100).toFixed();
|
||||
|
||||
// we can tick manually, for debugging
|
||||
@ -276,21 +279,12 @@ export class Aard {
|
||||
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.
|
||||
* This method should only ever be called from constructor.
|
||||
*/
|
||||
private init() {
|
||||
|
||||
|
||||
this.canvasStore = {
|
||||
main: this.createCanvas('main-gl')
|
||||
};
|
||||
@ -351,6 +345,7 @@ export class Aard {
|
||||
* Starts autodetection loop.
|
||||
*/
|
||||
start() {
|
||||
this.forceFullRecheck = true;
|
||||
if (this.videoData.resizer.lastAr.type === AspectRatioType.AutomaticUpdate) {
|
||||
// ensure first autodetection will run in any case
|
||||
this.videoData.resizer.lastAr = {type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr};
|
||||
@ -406,11 +401,14 @@ export class Aard {
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -419,6 +417,7 @@ export class Aard {
|
||||
resetAardTestResults(this.testResults);
|
||||
resetSamples(this.canvasSamples);
|
||||
this.main();
|
||||
this.forceFullRecheck = false;
|
||||
} else {
|
||||
}
|
||||
this.animationFrame = window.requestAnimationFrame( (ts: DOMHighResTimeStamp) => this.onAnimationFrame(ts));
|
||||
@ -477,7 +476,6 @@ export class Aard {
|
||||
);
|
||||
if (this.testResults.notLetterbox) {
|
||||
// TODO: reset aspect ratio to "AR not applied"
|
||||
console.log('NOT LETTERBOX!');
|
||||
this.testResults.lastStage = 1;
|
||||
break;
|
||||
}
|
||||
@ -486,19 +484,29 @@ export class Aard {
|
||||
// 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
|
||||
// detection is enabled, in which case we still run the subtitle test)
|
||||
this.checkLetterboxShrink(
|
||||
imageData,
|
||||
this.settings.active.arDetect.canvasDimensions.sampleCanvas.width,
|
||||
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) {
|
||||
this.checkLetterboxGrow(
|
||||
// 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(
|
||||
imageData,
|
||||
this.settings.active.arDetect.canvasDimensions.sampleCanvas.width,
|
||||
this.settings.active.arDetect.canvasDimensions.sampleCanvas.height
|
||||
);
|
||||
|
||||
if (! this.testResults.guardLine.invalidated) {
|
||||
this.checkLetterboxGrow(
|
||||
imageData,
|
||||
this.settings.active.arDetect.canvasDimensions.sampleCanvas.width,
|
||||
this.settings.active.arDetect.canvasDimensions.sampleCanvas.height
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Both need to be checked
|
||||
if (! (this.testResults.imageLine.invalidated || this.testResults.guardLine.invalidated)) {
|
||||
// TODO: ensure no aspect ratio changes happen
|
||||
@ -522,18 +530,34 @@ export class Aard {
|
||||
// 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
|
||||
|
||||
// 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
|
||||
// (as aspect ratio may have been set manually while autodetection was off)
|
||||
if (this.testResults.notLetterbox) {
|
||||
this.videoData.resizer.updateAr({
|
||||
type: AspectRatioType.AutomaticUpdate,
|
||||
ratio: 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) {
|
||||
console.info('aspect ratio not cettain.');
|
||||
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.videoData.resizer.setAr({type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: emit debug values if debugging is enabled
|
||||
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,
|
||||
'\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 (this.testResults.aspectRatioUpdated) {
|
||||
@ -543,12 +567,15 @@ export class Aard {
|
||||
offset: this.testResults.letterboxOffset
|
||||
});
|
||||
}
|
||||
|
||||
// if we got "no letterbox" OR aspectRatioUpdated
|
||||
} catch (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});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private getVideoPlaybackState(): VideoPlaybackState {
|
||||
try {
|
||||
if (this.video.ended) {
|
||||
@ -677,6 +704,8 @@ export class Aard {
|
||||
// NOTE: but maybe we could, if blackLevel can only get lower than
|
||||
// the default value.
|
||||
if (this.testResults.notLetterbox) {
|
||||
this.testResults.aspectRatioUncertain = false;
|
||||
|
||||
if (min < this.testResults.blackLevel) {
|
||||
this.testResults.blackLevel = min;
|
||||
this.testResults.blackThreshold = min + 16;
|
||||
@ -1611,6 +1640,7 @@ export class Aard {
|
||||
// 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;
|
||||
this.testResults.aspectRatioUncertainReason = 'TOP_ROW_MISMATCH';
|
||||
return;
|
||||
} else {
|
||||
// center matches one of the corners
|
||||
@ -1661,6 +1691,7 @@ export class Aard {
|
||||
// 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;
|
||||
this.testResults.aspectRatioUncertainReason += 'BOTTOM_ROW_MISMATCH';
|
||||
return;
|
||||
} else {
|
||||
// center matches one of the corners
|
||||
@ -1697,6 +1728,7 @@ export class Aard {
|
||||
|| candidateB < this.settings.active.arDetect.edgeDetection.thresholds.minQualitySecondEdge
|
||||
) {
|
||||
this.testResults.aspectRatioUncertain = true;
|
||||
this.testResults.aspectRatioUncertainReason = 'INSUFFICIENT_EDGE_DETECTION_QUALITY';
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1706,6 +1738,7 @@ export class Aard {
|
||||
|
||||
if (diff > maxOffset) {
|
||||
this.testResults.aspectRatioUncertain = true;
|
||||
this.testResults.aspectRatioUncertainReason = 'LETTERBOX_NOT_CENTERED_ENOUGH';
|
||||
return;
|
||||
}
|
||||
if (maxOffset > 2) {
|
||||
@ -1731,23 +1764,23 @@ export class Aard {
|
||||
const compensatedWidth = fileAr === canvasAr ? this.canvasStore.main.width : this.canvasStore.main.width * fileAr;
|
||||
|
||||
|
||||
console.log(`
|
||||
———— ASPECT RATIO CALCULATION: —————
|
||||
// console.log(`
|
||||
// ———— ASPECT RATIO CALCULATION: —————
|
||||
|
||||
canvas size: ${this.canvasStore.main.width} x ${this.canvasStore.main.height} (1:${this.canvasStore.main.width / this.canvasStore.main.height})
|
||||
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})
|
||||
// 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})
|
||||
// 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}
|
||||
// net video height: ${this.canvasStore.main.height - (this.testResults.letterboxWidth * 2)}
|
||||
|
||||
calculated aspect ratio -----
|
||||
// calculated aspect ratio -----
|
||||
|
||||
${compensatedWidth} ${compensatedWidth} ${compensatedWidth}
|
||||
——————————————— = —————————————— = —————— = ${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)}
|
||||
`);
|
||||
// ${compensatedWidth} ${compensatedWidth} ${compensatedWidth}
|
||||
// ——————————————— = —————————————— = —————— = ${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));
|
||||
|
7
src/ext/lib/aard/enums/aard-mode.enum.ts
Normal file
7
src/ext/lib/aard/enums/aard-mode.enum.ts
Normal file
@ -0,0 +1,7 @@
|
||||
enum AardMode {
|
||||
Continuous = 0,
|
||||
UntilDetection = 1,
|
||||
Timeout
|
||||
}
|
||||
|
||||
export default AardMode;
|
@ -2,6 +2,7 @@ import {VideoPlaybackState} from '../enums/video-playback-state.enum';
|
||||
|
||||
export interface AardStatus {
|
||||
aardActive: boolean,
|
||||
aardReducedPolling: boolean,
|
||||
checkInProgress: boolean,
|
||||
lastVideoStatus: VideoPlaybackState,
|
||||
|
||||
@ -10,6 +11,7 @@ export interface AardStatus {
|
||||
export function initAardStatus(): AardStatus {
|
||||
return {
|
||||
aardActive: false,
|
||||
aardReducedPolling: true,
|
||||
checkInProgress: false,
|
||||
lastVideoStatus: VideoPlaybackState.NotInitialized,
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ export interface AardTestResults {
|
||||
letterboxWidth: number,
|
||||
letterboxOffset: number,
|
||||
logoDetected: [boolean, boolean, boolean, boolean]
|
||||
aspectRatioUncertainReason?: string
|
||||
}
|
||||
|
||||
export function initAardTestResults(settings: AardSettings): AardTestResults {
|
||||
@ -91,4 +92,5 @@ export function resetAardTestResults(results: AardTestResults): void {
|
||||
results.letterboxWidth = 0;
|
||||
results.letterboxOffset = 0;
|
||||
results.aspectRatioUpdated = false;
|
||||
results.aspectRatioUncertainReason = null;
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
export interface AardTimers {
|
||||
nextFrameCheckTime: number;
|
||||
reducedPollingNextCheckTime: number;
|
||||
}
|
||||
|
||||
export function initAardTimers(): AardTimers {
|
||||
return {
|
||||
nextFrameCheckTime: 0
|
||||
nextFrameCheckTime: 0,
|
||||
reducedPollingNextCheckTime: 0,
|
||||
};
|
||||
}
|
||||
|
@ -148,14 +148,7 @@ class UI {
|
||||
|
||||
initMessaging() {
|
||||
// subscribe to events coming back to us. Unsubscribe if iframe vanishes.
|
||||
const messageHandlerFn = (message) => {
|
||||
if (!this.uiIframe?.contentWindow) {
|
||||
window.removeEventListener('message', messageHandlerFn);
|
||||
return;
|
||||
}
|
||||
this.handleMessage(message);
|
||||
}
|
||||
window.addEventListener('message', messageHandlerFn);
|
||||
window.addEventListener('message', this.messageHandlerFn);
|
||||
|
||||
|
||||
/* set up event bus tunnel from content script to UI — necessary if we want to receive
|
||||
@ -163,39 +156,45 @@ class UI {
|
||||
* necessary for UI display in the popup.
|
||||
*/
|
||||
|
||||
this.eventBus.subscribe(
|
||||
'uw-config-broadcast',
|
||||
this.eventBus.subscribeMulti(
|
||||
{
|
||||
function: (config, routingData) => {
|
||||
this.sendToIframe('uw-config-broadcast', config, routingData);
|
||||
}
|
||||
}
|
||||
);
|
||||
this.eventBus.subscribe(
|
||||
'uw-set-ui-state',
|
||||
{
|
||||
function: (config, routingData) => {
|
||||
if (config.globalUiVisible !== undefined) {
|
||||
if (this.isGlobal) {
|
||||
this.setUiVisibility(config.globalUiVisible);
|
||||
} else {
|
||||
this.setUiVisibility(!config.globalUiVisible);
|
||||
'uw-config-broadcast': {
|
||||
function: (config, routingData) => {
|
||||
console.log('sending config broadcast from eventBus subscription:', this.eventBus)
|
||||
this.sendToIframe('uw-config-broadcast', config, routingData);
|
||||
}
|
||||
},
|
||||
'uw-set-ui-state': {
|
||||
function: (config, routingData) => {
|
||||
if (config.globalUiVisible !== undefined) {
|
||||
if (this.isGlobal) {
|
||||
this.setUiVisibility(config.globalUiVisible);
|
||||
} else {
|
||||
this.setUiVisibility(!config.globalUiVisible);
|
||||
}
|
||||
}
|
||||
this.sendToIframe('uw-set-ui-state', {...config, isGlobal: this.isGlobal}, routingData);
|
||||
}
|
||||
},
|
||||
'uw-restore-ui-state': {
|
||||
function: (config, routingData) => {
|
||||
if (!this.isGlobal) {
|
||||
this.setUiVisibility(true);
|
||||
this.sendToIframe('uw-restore-ui-state', config, routingData);
|
||||
}
|
||||
}
|
||||
this.sendToIframe('uw-set-ui-state', {...config, isGlobal: this.isGlobal}, routingData);
|
||||
}
|
||||
}
|
||||
)
|
||||
this.eventBus.subscribe(
|
||||
'uw-restore-ui-state', {
|
||||
function: (config, routingData) => {
|
||||
if (!this.isGlobal) {
|
||||
this.setUiVisibility(true);
|
||||
this.sendToIframe('uw-restore-ui-state', config, routingData);
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
messageHandlerFn = (message) => {
|
||||
if (!this.uiIframe?.contentWindow) {
|
||||
window.removeEventListener('message', this.messageHandlerFn);
|
||||
return;
|
||||
}
|
||||
this.handleMessage(message);
|
||||
}
|
||||
|
||||
setUiVisibility(visible) {
|
||||
@ -280,6 +279,7 @@ class UI {
|
||||
this.eventBus.send(busCommand.action, busCommand.config, busCommand.routingData);
|
||||
break;
|
||||
case 'uwui-get-role':
|
||||
console.log('handle get role!');
|
||||
this.sendToIframeLowLevel('uwui-set-role', {role: this.isGlobal ? 'global' : 'player'});
|
||||
break;
|
||||
case 'uwui-interface-ready':
|
||||
@ -306,6 +306,7 @@ class UI {
|
||||
// because existence of UI is not guaranteed — UI is not shown when extension is inactive.
|
||||
// If extension is inactive due to "player element isn't big enough to justify it", however,
|
||||
// we can still receive eventBus messages.
|
||||
console.log('sending to iframe - low level.')
|
||||
if (this.element && this.uiIframe) {
|
||||
this.uiIframe.contentWindow?.postMessage(
|
||||
{
|
||||
@ -335,6 +336,7 @@ class UI {
|
||||
// }
|
||||
// routingData.crossedConnections.push(EventBusConnector.IframeBoundaryIn);
|
||||
|
||||
console.warn('send to iframe — uw bus tunnel. Action:', action, actionConfig)
|
||||
this.sendToIframeLowLevel(
|
||||
'uw-bus-tunnel',
|
||||
{
|
||||
@ -360,6 +362,8 @@ class UI {
|
||||
}
|
||||
|
||||
destroy() {
|
||||
window.removeEventListener('message', this.messageHandlerFn);
|
||||
this.eventBus.unsubscribeAll(this);
|
||||
// this.comms?.destroy();
|
||||
this.uiIframe?.remove();
|
||||
this.element?.remove();
|
||||
|
@ -36,18 +36,24 @@ export default class IframeManager {
|
||||
|
||||
if (this.isIframe) {
|
||||
window.addEventListener('beforeunload', this.destroy);
|
||||
|
||||
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(
|
||||
'uw-frame-register', {host: window.location.hostname}, {comms: {forwardTo: 'all-frames'}}
|
||||
);
|
||||
} else {
|
||||
this.eventBus.subscribe(
|
||||
'uw-frame-register', {function: (data, context) => this.handleIframeRegister(data, context)}
|
||||
);
|
||||
this.eventBus.subscribe(
|
||||
'uw-frame-destroyed', {function: (cmd, context) => this.handleFrameDestroyed(context)}
|
||||
this.eventBus.subscribeMulti(
|
||||
{
|
||||
'uw-frame-register': {function: (data, context) => this.handleIframeRegister(data, context)},
|
||||
'uw-frame-destroyed': {function: (cmd, context) => this.handleFrameDestroyed(context)}
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
// register all frames to re-register themselves
|
||||
@ -69,6 +75,7 @@ export default class IframeManager {
|
||||
}
|
||||
},
|
||||
)
|
||||
this.eventBus.unsubscribeAll(this);
|
||||
}
|
||||
|
||||
private handleIframePing(context) {
|
||||
|
@ -179,16 +179,22 @@ class PageInfo {
|
||||
}
|
||||
}
|
||||
|
||||
getVideos(host) {
|
||||
getVideos(): HTMLVideoElement[] {
|
||||
const videoQs = this.siteSettings.getCustomDOMQuerySelector('video');
|
||||
if (videoQs){
|
||||
const videos = document.querySelectorAll(videoQs) as NodeListOf<HTMLVideoElement>;
|
||||
let videos: HTMLVideoElement[] = [];
|
||||
|
||||
if (videos.length) {
|
||||
return videos;
|
||||
}
|
||||
if (videoQs){
|
||||
videos = Array.from(document.querySelectorAll(videoQs) as NodeListOf<HTMLVideoElement> ?? []);
|
||||
} else{
|
||||
videos = Array.from(document.getElementsByTagName('video') ?? []);
|
||||
}
|
||||
return document.getElementsByTagName('video');
|
||||
|
||||
// filter out videos that aren't big enough
|
||||
videos = videos.filter(
|
||||
(v: HTMLVideoElement) => v.clientHeight > 720 && v.clientWidth > 1208
|
||||
);
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
hasVideo() {
|
||||
@ -206,6 +212,7 @@ class PageInfo {
|
||||
// is there any video data objects that had their HTML elements removed but not yet
|
||||
// destroyed? We clean that up here.
|
||||
const orphans = this.videos.filter(x => !document.body.contains(x.element));
|
||||
|
||||
for (const orphan of orphans) {
|
||||
orphan.videoData.destroy();
|
||||
}
|
||||
@ -215,7 +222,7 @@ class PageInfo {
|
||||
|
||||
// add new videos
|
||||
try{
|
||||
let vids = this.getVideos(window.location.hostname);
|
||||
let vids = this.getVideos();
|
||||
|
||||
if(!vids || vids.length == 0){
|
||||
this.hasVideos = false;
|
||||
|
@ -80,6 +80,7 @@ class PlayerData {
|
||||
halted: boolean = true;
|
||||
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.
|
||||
isTooSmall: boolean = true;
|
||||
|
||||
//#region misc stuff
|
||||
extensionMode: any;
|
||||
@ -87,6 +88,8 @@ class PlayerData {
|
||||
private playerIdElement: any;
|
||||
private observer: ResizeObserver;
|
||||
|
||||
private trackChangesTimeout: any;
|
||||
|
||||
private ui: UI;
|
||||
|
||||
elementStack: any[] = [];
|
||||
@ -99,7 +102,8 @@ class PlayerData {
|
||||
}],
|
||||
'get-player-dimensions': [{
|
||||
function: () => {
|
||||
this.eventBus.send('uw-config-broadcast', {
|
||||
console.log('got get-player-dimensions');
|
||||
this.eventBus.send('—————————————————————————— uw-config-broadcast', {
|
||||
type: 'player-dimensions',
|
||||
data: this.dimensions
|
||||
});
|
||||
@ -157,20 +161,12 @@ class PlayerData {
|
||||
// do the rest
|
||||
this.invalid = false;
|
||||
this.element = this.getPlayer();
|
||||
this.isTooSmall = (this.element.clientWidth < 1208 || this.element.clientHeight < 720);
|
||||
|
||||
this.initEventBus();
|
||||
|
||||
// this.notificationService = new PlayerNotificationUi(this.element, this.settings, this.eventBus);
|
||||
if (this.videoData.settings.active.ui?.inPlayer?.enabled) {
|
||||
this.ui = new UI(
|
||||
'ultrawidifyUi',
|
||||
{
|
||||
parentElement: this.element,
|
||||
eventBus: this.eventBus,
|
||||
playerData: this,
|
||||
uiSettings: this.videoData.settings.active.ui
|
||||
}
|
||||
);
|
||||
}
|
||||
// we defer UI creation until player element is big enough
|
||||
// this happens in trackDimensionChanges!
|
||||
|
||||
this.dimensions = undefined;
|
||||
this.overlayNode = undefined;
|
||||
@ -233,10 +229,46 @@ class PlayerData {
|
||||
destroy() {
|
||||
document.removeEventListener('fullscreenchange', this.dimensionChangeListener);
|
||||
this.stopChangeDetection();
|
||||
this.ui?.destroy();
|
||||
this.notificationService?.destroy();
|
||||
}
|
||||
//#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
|
||||
* @param runLevel
|
||||
@ -253,11 +285,11 @@ class PlayerData {
|
||||
this.element.classList.remove(this.playerCssClass);
|
||||
}
|
||||
if (runLevel < RunLevel.UIOnly) {
|
||||
this.ui.disable();
|
||||
this.ui?.disable();
|
||||
}
|
||||
} else {
|
||||
if (runLevel >= RunLevel.UIOnly) {
|
||||
this.ui.enable();
|
||||
this.ui?.enable();
|
||||
this.startChangeDetection();
|
||||
}
|
||||
if (runLevel >= RunLevel.CustomCSSActive) {
|
||||
@ -313,12 +345,20 @@ class PlayerData {
|
||||
this.detectTheaterMode();
|
||||
}
|
||||
|
||||
// defer creating UI
|
||||
this.deferredUiInitialization(currentPlayerDimensions);
|
||||
|
||||
// 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 (
|
||||
this.dimensions?.width == currentPlayerDimensions.width
|
||||
&& this.dimensions?.height == currentPlayerDimensions.height
|
||||
) {
|
||||
this.eventBus.send('restore-ar', null);
|
||||
this.eventBus.send('delayed-restore-ar', {delay: 500});
|
||||
this.dimensions = currentPlayerDimensions;
|
||||
return;
|
||||
}
|
||||
@ -326,7 +366,7 @@ class PlayerData {
|
||||
// in every other case, we need to check if the player is still
|
||||
// big enough to warrant our extension running.
|
||||
this.handleSizeConstraints(currentPlayerDimensions);
|
||||
this.handleDimensionChanges(currentPlayerDimensions, this.dimensions);
|
||||
// this.handleDimensionChanges(currentPlayerDimensions, this.dimensions);
|
||||
|
||||
// Save current dimensions to avoid triggering this function pointlessly
|
||||
this.dimensions = currentPlayerDimensions;
|
||||
@ -343,14 +383,11 @@ class PlayerData {
|
||||
const canEnable = this.siteSettings.isEnabledForEnvironment(this.isFullscreen, this.isTheaterMode) === ExtensionMode.Enabled;
|
||||
|
||||
if (this.runLevel === RunLevel.Off && canEnable) {
|
||||
console.log('runLevel: off -> [anything]');
|
||||
this.eventBus.send('restore-ar', null);
|
||||
// must be called after
|
||||
this.handleDimensionChanges(currentPlayerDimensions, this.dimensions);
|
||||
} else if (!canEnable && this.runLevel !== RunLevel.Off) {
|
||||
// must be called before
|
||||
console.log('runLevel: [anything] -> off');
|
||||
|
||||
this.handleDimensionChanges(currentPlayerDimensions, this.dimensions);
|
||||
this.setRunLevel(RunLevel.Off);
|
||||
}
|
||||
@ -358,7 +395,6 @@ class PlayerData {
|
||||
|
||||
|
||||
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 ) {
|
||||
this.logger.log('info', 'debug', "[PlayerDetect] player size changed, but PlayerDetect is in disabled state. The player element is probably too small.");
|
||||
return;
|
||||
@ -374,7 +410,6 @@ class PlayerData {
|
||||
|| newDimensions?.height != oldDimensions?.height
|
||||
|| newDimensions?.fullscreen != oldDimensions?.fullscreen
|
||||
){
|
||||
console.log('dimensions changed + we are enabled. Sending restore-ar ...');
|
||||
// If player size changes, we restore aspect ratio
|
||||
this.eventBus.send('restore-ar', null);
|
||||
this.eventBus.send('delayed-restore-ar', {delay: 500});
|
||||
@ -383,6 +418,9 @@ class PlayerData {
|
||||
type: 'player-dimensions',
|
||||
data: newDimensions
|
||||
});
|
||||
|
||||
|
||||
this.isTooSmall = !newDimensions.fullscreen && (newDimensions.width < 1208 || newDimensions.height < 720);
|
||||
}
|
||||
}
|
||||
|
||||
@ -706,6 +744,7 @@ class PlayerData {
|
||||
// this populates this.elementStack fully
|
||||
this.getPlayer({verbose: true});
|
||||
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))});
|
||||
}
|
||||
|
||||
|
@ -88,15 +88,15 @@ class VideoData {
|
||||
}
|
||||
|
||||
private eventBusCommands = {
|
||||
'get-drm-status': [{
|
||||
'get-drm-status': {
|
||||
function: () => {
|
||||
this.hasDrm = hasDrm(this.video);
|
||||
this.eventBus.send('uw-config-broadcast', {type: 'drm-status', hasDrm: this.hasDrm});
|
||||
}
|
||||
}],
|
||||
'set-run-level': [{
|
||||
},
|
||||
'set-run-level': {
|
||||
function: (runLevel: RunLevel) => this.setRunLevel(runLevel)
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,19 +128,18 @@ class VideoData {
|
||||
height: this.video.offsetHeight,
|
||||
};
|
||||
|
||||
this.eventBus = new EventBus();
|
||||
if (!pageInfo.eventBus) {
|
||||
this.eventBus = new EventBus();
|
||||
} else {
|
||||
this.eventBus = pageInfo.eventBus;
|
||||
}
|
||||
|
||||
this.extensionStatus = new ExtensionStatus(siteSettings, pageInfo.eventBus, pageInfo.fsStatus);
|
||||
|
||||
if (pageInfo.eventBus) {
|
||||
this.eventBus.setUpstreamBus(pageInfo.eventBus);
|
||||
|
||||
for (const action in this.eventBusCommands) {
|
||||
for (const command of this.eventBusCommands[action]) {
|
||||
this.eventBus.subscribe(action, command);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.eventBus.subscribeMulti(
|
||||
this.eventBusCommands,
|
||||
this
|
||||
);
|
||||
|
||||
this.setupEventListeners();
|
||||
}
|
||||
@ -270,7 +269,6 @@ class VideoData {
|
||||
* Must be triggered on first action. TODO
|
||||
*/
|
||||
preparePage() {
|
||||
console.log('preparing page ...')
|
||||
this.injectBaseCss();
|
||||
this.pageInfo.initMouseActionHandler(this);
|
||||
|
||||
@ -365,7 +363,7 @@ class VideoData {
|
||||
|
||||
this.eventBus.send('set-run-level', RunLevel.Off);
|
||||
this.destroyed = true;
|
||||
this.eventBus?.unsetUpstreamBus();
|
||||
this.eventBus.unsubscribeAll(this);
|
||||
|
||||
try {
|
||||
this.aard.stop();
|
||||
@ -389,16 +387,12 @@ class VideoData {
|
||||
|
||||
|
||||
setRunLevel(runLevel: RunLevel, options?: {fromPlayer?: boolean}) {
|
||||
console.log('setting new runlevel for videodata. current:', this.runLevel, 'new', runLevel)
|
||||
if (this.runLevel === runLevel) {
|
||||
return; // also no need to propagate to the player
|
||||
}
|
||||
|
||||
console.log('setting run level ...')
|
||||
|
||||
// Run level decreases towards 'off'
|
||||
if (this.runLevel > runLevel) {
|
||||
console.log('decreasing css.')
|
||||
if (runLevel < RunLevel.CustomCSSActive) {
|
||||
this.video.classList.remove(this.baseCssName);
|
||||
this.video.classList.remove(this.userCssClassName);
|
||||
|
@ -63,9 +63,11 @@ class Resizer {
|
||||
|
||||
_lastAr: Ar = {type: AspectRatioType.Initial};
|
||||
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;
|
||||
// emit updates for UI when setting lastAr
|
||||
this.eventBus.send('uw-config-broadcast', {type: 'ar', config: x})
|
||||
}
|
||||
get lastAr() {
|
||||
return this._lastAr;
|
||||
|
Loading…
Reference in New Issue
Block a user