Re-add legacy AR detect
This commit is contained in:
parent
ddffbe92a0
commit
c0c29759fa
@ -8,6 +8,7 @@
|
|||||||
* Setting inheritance/overriding is not thoroughly tested and may be full of edge cases.
|
* Setting inheritance/overriding is not thoroughly tested and may be full of edge cases.
|
||||||
* Added validation to custom aspect ratio entry menu. Corrected parsing of aspect ratios given in the X:Y format, even though aspect ratios should be ideally given as a single number.
|
* Added validation to custom aspect ratio entry menu. Corrected parsing of aspect ratios given in the X:Y format, even though aspect ratios should be ideally given as a single number.
|
||||||
* Autodetection can now scan for subtitles.
|
* Autodetection can now scan for subtitles.
|
||||||
|
* New experimental autodetection (which is secretly just slightly modified subtitle check)
|
||||||
|
|
||||||
### v6.3.0
|
### v6.3.0
|
||||||
* Added zoom segment to in-player UI and popup.
|
* Added zoom segment to in-player UI and popup.
|
||||||
|
|||||||
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ultrawidify",
|
"name": "ultrawidify",
|
||||||
"version": "6.3.97",
|
"version": "6.3.98",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ultrawidify",
|
"name": "ultrawidify",
|
||||||
"version": "6.3.97",
|
"version": "6.3.98",
|
||||||
"description": "Aspect ratio fixer for youtube and other sites, with automatic aspect ratio detection. Supports ultrawide and other ratios.",
|
"description": "Aspect ratio fixer for youtube and other sites, with automatic aspect ratio detection. Supports ultrawide and other ratios.",
|
||||||
"author": "Tamius Han <tamius.han@gmail.com>",
|
"author": "Tamius Han <tamius.han@gmail.com>",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -90,6 +90,7 @@ export interface AardSubtitleScanOptions {
|
|||||||
|
|
||||||
export interface AardSettings {
|
export interface AardSettings {
|
||||||
aardType: 'webgl' | 'legacy' | 'auto';
|
aardType: 'webgl' | 'legacy' | 'auto';
|
||||||
|
useLegacy: boolean,
|
||||||
|
|
||||||
earlyStopOptions: {
|
earlyStopOptions: {
|
||||||
stopAfterFirstDetection: boolean;
|
stopAfterFirstDetection: boolean;
|
||||||
@ -183,6 +184,131 @@ export interface AardSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AardLegacySettings {
|
||||||
|
aardType: 'webgl' | 'legacy' | 'auto';
|
||||||
|
|
||||||
|
earlyStopOptions: {
|
||||||
|
stopAfterFirstDetection: boolean;
|
||||||
|
stopAfterTimeout: boolean;
|
||||||
|
stopTimeout: number;
|
||||||
|
},
|
||||||
|
|
||||||
|
polling: {
|
||||||
|
runInBackgroundTabs: AardPollingOptions;
|
||||||
|
runOnSmallVideos: AardPollingOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
tickrate: number, // 1 tick every this many milliseconds
|
||||||
|
},
|
||||||
|
|
||||||
|
subtitles: AardSubtitleScanOptions,
|
||||||
|
|
||||||
|
autoDisable: { // settings for automatically disabling the extension
|
||||||
|
onFirstChange: boolean,
|
||||||
|
ifNotChanged: boolean,
|
||||||
|
ifNotChangedTimeout: number,
|
||||||
|
ifSubtitles: boolean;
|
||||||
|
},
|
||||||
|
canvasDimensions: {
|
||||||
|
blackframeCanvas: { // smaller than sample canvas, blackframe canvas is used to recon for black frames
|
||||||
|
// it's not used to detect aspect ratio by itself, so it can be tiny af
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
},
|
||||||
|
sampleCanvas: { // size of image sample for detecting aspect ratio. Bigger size means more accurate results,
|
||||||
|
// at the expense of performance
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
blackLevels: {
|
||||||
|
defaultBlack: number, // By default, pixels darker than this are considered black.
|
||||||
|
// (If detection algorithm detects darker blacks, black is considered darkest detected pixel)
|
||||||
|
blackTolerance: number, // If pixel is more than this much brighter than blackLevel, it's considered not black
|
||||||
|
// It is not considered a valid image detection if gradient detection is enabled
|
||||||
|
imageDelta: number, // When gradient detection is enabled, pixels this much brighter than black skip gradient detection
|
||||||
|
}
|
||||||
|
sampling: {
|
||||||
|
edgePosition: number; // % of width (max 0.33). Pixels up to this far away from either edge may contain logo.
|
||||||
|
staticCols: number, // we take a column at [0-n]/n-th parts along the width and sample it
|
||||||
|
randomCols: number, // we add this many randomly selected columns to the static columns
|
||||||
|
staticRows: number, // forms grid with staticSampleCols. Determined in the same way. For black frame checks,
|
||||||
|
},
|
||||||
|
|
||||||
|
// pls deprecate and move things used
|
||||||
|
edgeDetection: {
|
||||||
|
slopeTestWidth: number,
|
||||||
|
gradientTestSamples: number, // we check this many pixels below (or above) the suspected edge to check for gradient
|
||||||
|
gradientTestBlackThreshold: number, // if pixel in test sample is brighter than that, we aren't looking at gradient
|
||||||
|
gradientTestDeltaThreshold: number, // if delta between two adjacent pixels in gradient test exceeds this, it's not gradient
|
||||||
|
|
||||||
|
thresholds: {
|
||||||
|
edgeDetectionLimit: number, // during scanning of the edge, quit after edge gets detected at this many points
|
||||||
|
minQualitySingleEdge: number, // At least one of the detected must reach this quality
|
||||||
|
minQualitySecondEdge: number, // The other edge must reach this quality (must be smaller or equal to single edge quality)
|
||||||
|
}
|
||||||
|
|
||||||
|
gradientThreshold: number, // if more than this percentage (0-1) is detected as gradient, we mark edge as gradient
|
||||||
|
gradientTestMinDelta: number, // if difference between test row and before row is MORE than this -> not gradient
|
||||||
|
gradientTestMinDeltaAfter: number, // if difference between test row and after row is LESS than this -> not gradient
|
||||||
|
gradientTestMaxDeltaAfter: number, // if difference between test row and after row is MORE than this -> not gradient
|
||||||
|
maxLetterboxOffset: number, // Upper and lower letterbox can be different by this many (% of height)
|
||||||
|
|
||||||
|
// Previous iteration variables VVVV
|
||||||
|
sampleWidth: number, // we take a sample this wide for edge detection
|
||||||
|
detectionThreshold: number, // sample needs to have this many non-black pixels to be a valid edge
|
||||||
|
confirmationThreshold: number, //
|
||||||
|
singleSideConfirmationThreshold: number, // we need this much edges (out of all samples, not just edges) in order
|
||||||
|
// to confirm an edge in case there's no edges on top or bottom (other
|
||||||
|
// than logo, of course)
|
||||||
|
logoThreshold: number, // if edge candidate sits with count greater than this*all_samples, it can't be logo
|
||||||
|
// or watermark.
|
||||||
|
edgeTolerancePx?: number, // we check for black edge violation this far from detection point
|
||||||
|
edgeTolerancePercent?: number, // we check for black edge detection this % of height from detection point. unused
|
||||||
|
middleIgnoredArea: number, // we ignore this % of canvas height towards edges while detecting aspect ratios
|
||||||
|
minColsForSearch: number, // if we hit the edge of blackbars for all but this many columns (%-wise), we don't
|
||||||
|
// continue with search. It's pointless, because black edge is higher/lower than we
|
||||||
|
// are now. (NOTE: keep this less than 1 in case we implement logo detection)
|
||||||
|
edgeMismatchTolerancePx: number,// corners and center are considered equal if they differ by at most this many px
|
||||||
|
|
||||||
|
|
||||||
|
minValidImage: number, // if more than this % (0-1) of row is image, we confirm image regardless of other criteria except gradient
|
||||||
|
maxEdgeSegments: number, // if edge has more than this many segments, we consider it unreliable
|
||||||
|
minEdgeSegmentSize: number,
|
||||||
|
averageEdgeThreshold: number, // average edge must be this many px
|
||||||
|
},
|
||||||
|
|
||||||
|
letterboxOrientationScan: {
|
||||||
|
letterboxLimit: number, // how many non-black pixels we can detect before ruling out letterbox
|
||||||
|
pillarboxLimit: number, // how many non-black pixels we can detect before ruling out pillarbox
|
||||||
|
}
|
||||||
|
|
||||||
|
pillarTest: {
|
||||||
|
ignoreThinPillarsPx: number, // ignore pillars that are less than this many pixels thick.
|
||||||
|
allowMisaligned: number // left and right edge can vary this much (%)
|
||||||
|
},
|
||||||
|
textLineTest: {
|
||||||
|
nonTextPulse: number, // if a single continuous pulse has this many non-black pixels, we aren't dealing
|
||||||
|
// with text. This value is relative to canvas width (%)
|
||||||
|
pulsesToConfirm: number, // this is a threshold to confirm we're seeing text.
|
||||||
|
pulsesToConfirmIfHalfBlack: number, // this is the threshold to confirm we're seeing text if longest black pulse
|
||||||
|
// is over 50% of the canvas width
|
||||||
|
testRowOffset: number // we test this % of height from detected edge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
interface DevSettings {
|
interface DevSettings {
|
||||||
loadFromSnapshot: boolean,
|
loadFromSnapshot: boolean,
|
||||||
}
|
}
|
||||||
@ -194,7 +320,8 @@ interface SettingsInterface {
|
|||||||
}
|
}
|
||||||
dev: DevSettings,
|
dev: DevSettings,
|
||||||
|
|
||||||
arDetect: AardSettings,
|
aardLegacy: AardLegacySettings,
|
||||||
|
aard: AardSettings,
|
||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
inPlayer: {
|
inPlayer: {
|
||||||
|
|||||||
@ -11,19 +11,29 @@
|
|||||||
|
|
||||||
<div class="settings-segment">
|
<div class="settings-segment">
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">Autodetection mode (requires page reload)</div>
|
||||||
|
<div class="select">
|
||||||
|
<select v-model="settings.active.aard.useLegacy" @change="aardLegacyModeChanged">
|
||||||
|
<option :value="true">Legacy</option>
|
||||||
|
<option :value="false">Experimental</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="label">Autodetection frequency (time between samples)</div>
|
<div class="label">Autodetection frequency (time between samples)</div>
|
||||||
<div class="range-input">
|
<div class="range-input">
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
:value="Math.log(settings.active.arDetect.timers.playing)"
|
:value="Math.log(aardSettings.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="0.01"
|
step="0.01"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
v-model="settings.active.arDetect.timers.playing"
|
v-model="aardSettings.timers.playing"
|
||||||
@change="setArCheckFrequency($event.target.value)"
|
@change="setArCheckFrequency($event.target.value)"
|
||||||
class="input"
|
class="input"
|
||||||
type="text"
|
type="text"
|
||||||
@ -37,14 +47,14 @@
|
|||||||
<div class="range-input">
|
<div class="range-input">
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
:value="Math.log(settings.active.arDetect.timers.playingReduced)"
|
:value="Math.log(aardSettings.timers.playingReduced)"
|
||||||
@change="setArCheckFrequency($event.target.value, 'playingReduced')"
|
@change="setArCheckFrequency($event.target.value, 'playingReduced')"
|
||||||
min="2.3"
|
min="2.3"
|
||||||
max="9.3"
|
max="9.3"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
v-model="settings.active.arDetect.timers.playingReduced"
|
v-model="aardSettings.timers.playingReduced"
|
||||||
@change="setArCheckFrequency($event.target.value, 'playingReduced')"
|
@change="setArCheckFrequency($event.target.value, 'playingReduced')"
|
||||||
class="input"
|
class="input"
|
||||||
type="text"
|
type="text"
|
||||||
@ -56,64 +66,64 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="label">Poll for aspect ratio changes in background tabs:</div>
|
<div class="label">Poll for aspect ratio changes in background tabs:</div>
|
||||||
<div class="select">
|
<div class="select">
|
||||||
<select v-model="settings.active.arDetect.polling.runInBackgroundTabs" @change="settings.saveWithoutReload">
|
<select v-model="aardSettings.polling.runInBackgroundTabs" @change="settings.saveWithoutReload">
|
||||||
<option :value="AardPollingOptions.No">Never</option>
|
<option :value="AardPollingOptions.No">Never</option>
|
||||||
<option :value="AardPollingOptions.Reduced">Use reduced polling rate</option>
|
<option :value="AardPollingOptions.Reduced">Use reduced polling rate</option>
|
||||||
<option :value="AardPollingOptions.Full">Use normal polling rate</option>
|
<option :value="AardPollingOptions.Full">Use normal polling rate</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="settings.active.arDetect.polling.runInBackgroundTabs === AardPollingOptions.Full" class="hint warn">
|
<div v-if="aardSettings.polling.runInBackgroundTabs === AardPollingOptions.Full" class="hint warn">
|
||||||
Using normal polling rate in background tabs is NOT recommended.
|
Using normal polling rate in background tabs is NOT recommended.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="label">Poll for aspect ratio changes in small players:</div>
|
<div class="label">Poll for aspect ratio changes in small players:</div>
|
||||||
<div class="select">
|
<div class="select">
|
||||||
<select v-model="settings.active.arDetect.polling.runOnSmallVideos" @change="settings.saveWithoutReload">
|
<select v-model="aardSettings.polling.runOnSmallVideos" @change="settings.saveWithoutReload">
|
||||||
<option :value="AardPollingOptions.No">Never</option>
|
<option :value="AardPollingOptions.No">Never</option>
|
||||||
<option :value="AardPollingOptions.Reduced">Use reduced polling rate</option>
|
<option :value="AardPollingOptions.Reduced">Use reduced polling rate</option>
|
||||||
<option :value="AardPollingOptions.Full">Use normal polling rate</option>
|
<option :value="AardPollingOptions.Full">Use normal polling rate</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="settings.active.arDetect.polling.runOnSmallVideos === AardPollingOptions.Full" class="hint warn">
|
<div v-if="aardSettings.polling.runOnSmallVideos === AardPollingOptions.Full" class="hint warn">
|
||||||
Using normal polling rate on small videos is NOT recommended.
|
Using normal polling rate on small videos is NOT recommended.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="label">Stop autodetection after first detection:</div>
|
<div class="label">Stop autodetection after first detection:</div>
|
||||||
<div class="select">
|
<div class="select">
|
||||||
<select v-model="settings.active.arDetect.autoDisable.onFirstChange" @change="settings.saveWithoutReload">
|
<select v-model="aardSettings.autoDisable.onFirstChange" @change="settings.saveWithoutReload">
|
||||||
<option :value="true">Yes</option>
|
<option :value="true">Yes</option>
|
||||||
<option :value="false">No</option>
|
<option :value="false">No</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field" :class="{disabled: settings.active.arDetect.autoDisable.onFirstChange}">
|
<div class="field" :class="{disabled: aardSettings.autoDisable.onFirstChange}">
|
||||||
<div class="label">Stop autodetection if aspect ratio doesn't change for some time:</div>
|
<div class="label">Stop autodetection if aspect ratio doesn't change for some time:</div>
|
||||||
<div class="select">
|
<div class="select">
|
||||||
<select v-model="settings.active.arDetect.autoDisable.ifNotChanged" @change="settings.saveWithoutReload">
|
<select v-model="aardSettings.autoDisable.ifNotChanged" @change="settings.saveWithoutReload">
|
||||||
<option :value="true">Yes</option>
|
<option :value="true">Yes</option>
|
||||||
<option :value="false">No</option>
|
<option :value="false">No</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="settings.active.arDetect.autoDisable.ifNotChanged" class="field">
|
<div v-if="aardSettings.autoDisable.ifNotChanged" class="field">
|
||||||
<div class="label">Stop autodetection if aspect ratio doesn't change for:</div>
|
<div class="label">Stop autodetection if aspect ratio doesn't change for:</div>
|
||||||
<div class="range-input">
|
<div class="range-input">
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
:value="Math.log(settings.active.arDetect.autoDisable.ifNotChangedTimeout / 10)"
|
:value="Math.log(aardSettings.autoDisable.ifNotChangedTimeout / 10)"
|
||||||
@change="setAutoDisableTimeout($event.target.value, 10)"
|
@change="setAutoDisableTimeout($event.target.value, 10)"
|
||||||
min="2.3"
|
min="2.3"
|
||||||
max="9.3"
|
max="9.3"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
:value="settings.active.arDetect.autoDisable.ifNotChangedTimeout / 1000"
|
:value="aardSettings.autoDisable.ifNotChangedTimeout / 1000"
|
||||||
@change="setAutoDisableTimeout($event.target.value, 1000)"
|
@change="setAutoDisableTimeout($event.target.value, 1000)"
|
||||||
class="input"
|
class="input"
|
||||||
type="text"
|
type="text"
|
||||||
@ -125,7 +135,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="label">Autodetection canvas type:</div>
|
<div class="label">Autodetection canvas type:</div>
|
||||||
<div class="select">
|
<div class="select">
|
||||||
<select v-model="settings.active.arDetect.aardType" @change="settings.saveWithoutReload">
|
<select v-model="aardSettings.aardType" @change="settings.saveWithoutReload">
|
||||||
<option value="auto">Automatic</option>
|
<option value="auto">Automatic</option>
|
||||||
<option value="webgl">WebGL only</option>
|
<option value="webgl">WebGL only</option>
|
||||||
<option value="legacy">Legacy / fallback</option>
|
<option value="legacy">Legacy / fallback</option>
|
||||||
@ -136,7 +146,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="label">Maximum allowed vertical video misalignment:</div>
|
<div class="label">Maximum allowed vertical video misalignment:</div>
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<input v-model="settings.active.arDetect.allowedMisaligned" />
|
<input v-model="aardSettings.allowedMisaligned" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hint">
|
<div class="hint">
|
||||||
@ -154,7 +164,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="label">Subtitle detection:</div>
|
<div class="label">Subtitle detection:</div>
|
||||||
<div class="select">
|
<div class="select">
|
||||||
<select v-model="settings.active.arDetect.subtitles.subtitleCropMode" @change="settings.saveWithoutReload">
|
<select v-model="aardSettings.subtitles.subtitleCropMode" @change="settings.saveWithoutReload">
|
||||||
<option :value="AardSubtitleCropMode.DisableScan">Do not detect subtitles</option>
|
<option :value="AardSubtitleCropMode.DisableScan">Do not detect subtitles</option>
|
||||||
<option :value="AardSubtitleCropMode.ResetAR">Do not crop while subtitles are on screen</option>
|
<option :value="AardSubtitleCropMode.ResetAR">Do not crop while subtitles are on screen</option>
|
||||||
<option :value="AardSubtitleCropMode.ResetAndDisable">Stop autodetection if subtitles are detected</option>
|
<option :value="AardSubtitleCropMode.ResetAndDisable">Stop autodetection if subtitles are detected</option>
|
||||||
@ -163,19 +173,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="settings.active.arDetect.subtitles.subtitleCropMode === AardSubtitleCropMode.ResetAR" class="field">
|
<div v-if="aardSettings.subtitles.subtitleCropMode === AardSubtitleCropMode.ResetAR" class="field">
|
||||||
<div class="label">Wait before resuming detection:</div>
|
<div class="label">Wait before resuming detection:</div>
|
||||||
<div class="range-input">
|
<div class="range-input">
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
:value="Math.log(settings.active.arDetect.subtitles.resumeAfter / 10)"
|
:value="Math.log(aardSettings.subtitles.resumeAfter / 10)"
|
||||||
@change="setSubtitleTimeout($event.target.value, 10)"
|
@change="setSubtitleTimeout($event.target.value, 10)"
|
||||||
min="2.3"
|
min="2.3"
|
||||||
max="9.3"
|
max="9.3"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
:value="settings.active.arDetect.subtitles.resumeAfter / 1000"
|
:value="aardSettings.subtitles.resumeAfter / 1000"
|
||||||
@change="setSubtitleTimeout($event.target.value, 1000)"
|
@change="setSubtitleTimeout($event.target.value, 1000)"
|
||||||
class="input"
|
class="input"
|
||||||
type="text"
|
type="text"
|
||||||
@ -261,6 +271,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
aardSettings() {
|
||||||
|
return this.settings.active.aard.useLegacy ? this.settings.active.aardLegacy : this.settings.active.aard;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.eventBus.subscribe(
|
this.eventBus.subscribe(
|
||||||
@ -285,15 +298,15 @@ export default {
|
|||||||
BrowserDetect.runtime.openOptionsPage();
|
BrowserDetect.runtime.openOptionsPage();
|
||||||
},
|
},
|
||||||
setArCheckFrequency(event, timer) {
|
setArCheckFrequency(event, timer) {
|
||||||
this.settings.active.arDetect.timers[timer ?? 'playing'] = Math.floor(Math.pow(Math.E, event));
|
this.aardSettings.timers[timer ?? 'playing'] = Math.floor(Math.pow(Math.E, event));
|
||||||
this.settings.saveWithoutReload();
|
this.settings.saveWithoutReload();
|
||||||
},
|
},
|
||||||
setAutoDisableTimeout(event, multiplier) {
|
setAutoDisableTimeout(event, multiplier) {
|
||||||
this.settings.active.arDetect.autoDisable.ifNotChangedTimeout = Math.floor(Math.pow(Math.E, event)) * multiplier;
|
this.aardSettings.autoDisable.ifNotChangedTimeout = Math.floor(Math.pow(Math.E, event)) * multiplier;
|
||||||
this.settings.saveWithoutReload();
|
this.settings.saveWithoutReload();
|
||||||
},
|
},
|
||||||
setSubtitleTimeout(event, multiplier) {
|
setSubtitleTimeout(event, multiplier) {
|
||||||
this.settings.active.arDetect.subtitles.resumeAfter = Math.floor(Math.pow(Math.E, event)) * multiplier;
|
this.aardSettings.subtitles.resumeAfter = Math.floor(Math.pow(Math.E, event)) * multiplier;
|
||||||
this.settings.saveWithoutReload();
|
this.settings.saveWithoutReload();
|
||||||
},
|
},
|
||||||
refreshGraph() {
|
refreshGraph() {
|
||||||
@ -311,6 +324,9 @@ export default {
|
|||||||
saveDebugUiSettings() {
|
saveDebugUiSettings() {
|
||||||
this.settings.active.ui.dev.aardDebugOverlay = JSON.parse(JSON.stringify(this.settingsJson));
|
this.settings.active.ui.dev.aardDebugOverlay = JSON.parse(JSON.stringify(this.settingsJson));
|
||||||
this.settings.saveWithoutReload();
|
this.settings.saveWithoutReload();
|
||||||
|
},
|
||||||
|
aardLegacyModeChanged() {
|
||||||
|
this.settings.save();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,10 @@
|
|||||||
<li>Embedded sites now inherit settings of the parent frame. <small>However, this hasn't been tested for all edge cases and may contain bugs.</small></li>
|
<li>Embedded sites now inherit settings of the parent frame. <small>However, this hasn't been tested for all edge cases and may contain bugs.</small></li>
|
||||||
<li>Added validation to custom aspect ratio entry menu. Corrected parsing of aspect ratios given in the X:Y format, even though aspect ratios should be ideally given as a single number.</li>
|
<li>Added validation to custom aspect ratio entry menu. Corrected parsing of aspect ratios given in the X:Y format, even though aspect ratios should be ideally given as a single number.</li>
|
||||||
<li>Autodetection can be set to stop after first aspect ratio detection, or after a period of no changes.</li>
|
<li>Autodetection can be set to stop after first aspect ratio detection, or after a period of no changes.</li>
|
||||||
|
<li>
|
||||||
|
There is a new, experiental mode for autodetection. At this point, it can be manually enabled in the autodetection settings. It will become the default option in 2026.<br/>
|
||||||
|
If you enable experimental mode, please consider reporting problems <a href="https://github.com/tamius-han/ultrawidify/issues/291" target="_blank">in this thread</a> on Github.
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -255,17 +255,25 @@ const ExtensionConfPatch = Object.freeze([
|
|||||||
{
|
{
|
||||||
forVersion: '6.3.93',
|
forVersion: '6.3.93',
|
||||||
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => {
|
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => {
|
||||||
userOptions.arDetect.polling = defaultOptions.arDetect.polling;
|
(userOptions as any).arDetect.polling = defaultOptions.aard.polling;
|
||||||
userOptions.arDetect.subtitles = defaultOptions.arDetect.subtitles;
|
(userOptions as any).arDetect.subtitles = defaultOptions.aard.subtitles;
|
||||||
userOptions.arDetect.autoDisable = defaultOptions.arDetect.autoDisable;
|
(userOptions as any).arDetect.autoDisable = defaultOptions.aard.autoDisable;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
forVersion: '6.3.97',
|
forVersion: '6.3.98',
|
||||||
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => {
|
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => {
|
||||||
userOptions.arDetect.letterboxOrientationScan = defaultOptions.arDetect.letterboxOrientationScan;
|
(userOptions as any).arDetect.letterboxOrientationScan = defaultOptions.aard.letterboxOrientationScan;
|
||||||
userOptions.arDetect.edgeDetection = defaultOptions.arDetect.edgeDetection;
|
(userOptions as any).arDetect.edgeDetection = defaultOptions.aard.edgeDetection;
|
||||||
userOptions.arDetect.subtitles = defaultOptions.arDetect.subtitles;
|
(userOptions as any).arDetect.subtitles = defaultOptions.aard.subtitles;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
forVersion: '6.3.98',
|
||||||
|
upgradeFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => {
|
||||||
|
userOptions.aard = defaultOptions.aard;
|
||||||
|
userOptions.aardLegacy = defaultOptions.aardLegacy;
|
||||||
|
delete (userOptions as any).arDetect;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@ const ExtensionConf: SettingsInterface = {
|
|||||||
loadFromSnapshot: false,
|
loadFromSnapshot: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
arDetect: {
|
aardLegacy: {
|
||||||
aardType: 'auto',
|
aardType: 'auto',
|
||||||
|
|
||||||
polling: {
|
polling: {
|
||||||
@ -88,6 +88,140 @@ const ExtensionConf: SettingsInterface = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
blackLevels: {
|
||||||
|
defaultBlack: 16,
|
||||||
|
blackTolerance: 4,
|
||||||
|
imageDelta: 16,
|
||||||
|
},
|
||||||
|
sampling: {
|
||||||
|
edgePosition: 0.25,
|
||||||
|
staticCols: 16, // we take a column at [0-n]/n-th parts along the width and sample it
|
||||||
|
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
|
||||||
|
},
|
||||||
|
edgeDetection: {
|
||||||
|
slopeTestWidth: 8,
|
||||||
|
gradientTestSamples: 8,
|
||||||
|
gradientTestBlackThreshold: 16,
|
||||||
|
gradientTestDeltaThreshold: 32,
|
||||||
|
gradientTestMinDelta: 8,
|
||||||
|
|
||||||
|
thresholds: {
|
||||||
|
edgeDetectionLimit: 12,
|
||||||
|
minQualitySingleEdge: 6,
|
||||||
|
minQualitySecondEdge: 3,
|
||||||
|
},
|
||||||
|
|
||||||
|
maxLetterboxOffset: 0.1,
|
||||||
|
|
||||||
|
sampleWidth: 8, // we take a sample this wide for edge detection
|
||||||
|
detectionThreshold: 4, // sample needs to have this many non-black pixels to be a valid edge
|
||||||
|
confirmationThreshold: 1, //
|
||||||
|
singleSideConfirmationThreshold: 3, // we need this much edges (out of all samples, not just edges) in order
|
||||||
|
// to confirm an edge in case there's no edges on top or bottom (other
|
||||||
|
// than logo, of course)
|
||||||
|
logoThreshold: 0.15, // if edge candidate sits with count greater than this*all_samples, it can't be logo
|
||||||
|
// or watermark.
|
||||||
|
edgeTolerancePx: 1, // we check for black edge violation this far from detection point
|
||||||
|
edgeTolerancePercent: null, // we check for black edge detection this % of height from detection point. unused
|
||||||
|
middleIgnoredArea: 0.2, // we ignore this % of canvas height towards edges while detecting aspect ratios
|
||||||
|
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
|
||||||
|
// 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
|
||||||
|
|
||||||
|
gradientThreshold: 0.5, // if more than this percentage (0-1) is detected as gradient, we mark edge as gradient
|
||||||
|
gradientTestMinDeltaAfter: 2, // if difference between test row and after row is LESS than this -> not gradient
|
||||||
|
gradientTestMaxDeltaAfter: 12, // if difference between test row and after row is MORE than this -> not gradient
|
||||||
|
|
||||||
|
minValidImage: 0.7, // if more than this % (0-1) of row is image, we confirm image regardless of other criteria except gradient
|
||||||
|
maxEdgeSegments: 8, // if edge has more than this many segments, we consider it unreliable
|
||||||
|
minEdgeSegmentSize: 2,
|
||||||
|
averageEdgeThreshold: 16, // average(ish) edge must be this many px
|
||||||
|
},
|
||||||
|
pillarTest: {
|
||||||
|
ignoreThinPillarsPx: 5, // ignore pillars that are less than this many pixels thick.
|
||||||
|
allowMisaligned: 0.05 // left and right edge can vary this much (%)
|
||||||
|
},
|
||||||
|
textLineTest: {
|
||||||
|
nonTextPulse: 0.10, // if a single continuous pulse has this many non-black pixels, we aren't dealing
|
||||||
|
// with text. This value is relative to canvas width (%)
|
||||||
|
pulsesToConfirm: 10, // this is a threshold to confirm we're seeing text.
|
||||||
|
pulsesToConfirmIfHalfBlack: 5, // this is the threshold to confirm we're seeing text if longest black pulse
|
||||||
|
// is over 50% of the canvas width
|
||||||
|
testRowOffset: 0.02 // we test this % of height from detected edge
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
aard: {
|
||||||
|
aardType: 'auto',
|
||||||
|
useLegacy: true,
|
||||||
|
|
||||||
|
polling: {
|
||||||
|
runInBackgroundTabs: AardPollingOptions.Reduced,
|
||||||
|
runOnSmallVideos: AardPollingOptions.Reduced
|
||||||
|
},
|
||||||
|
|
||||||
|
letterboxOrientationScan: {
|
||||||
|
letterboxLimit: 8,
|
||||||
|
pillarboxLimit: 8
|
||||||
|
},
|
||||||
|
|
||||||
|
subtitles: {
|
||||||
|
subtitleCropMode: AardSubtitleCropMode.ResetAR,
|
||||||
|
resumeAfter: 5000,
|
||||||
|
scanSpacing: 5,
|
||||||
|
scanMargin: 0.25,
|
||||||
|
maxValidLetter: 24,
|
||||||
|
subtitleSubpixelThresholdOff: 8,
|
||||||
|
subtitleSubpixelThresholdOn: 192,
|
||||||
|
minDetections: 8,
|
||||||
|
minImageLineDetections: 8,
|
||||||
|
|
||||||
|
refiningScanSpacing: 8,
|
||||||
|
refiningScanInitialIterations: 12,
|
||||||
|
|
||||||
|
maxPotentialSubtitleMisalignment: 32,
|
||||||
|
},
|
||||||
|
|
||||||
|
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.0125,// 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,
|
||||||
|
tickrate: 10, // 1 tick every this many milliseconds
|
||||||
|
},
|
||||||
|
autoDisable: { // settings for automatically disabling the extension
|
||||||
|
onFirstChange: false, // disable once we have a stable aspect ratio
|
||||||
|
ifNotChanged: false, // disable if Ar hasn't changed for this long
|
||||||
|
ifNotChangedTimeout: 20000, // if user enables ifNotChangedTimeout, we default to 20s
|
||||||
|
ifSubtitles: false, // disable if subtitles are detected
|
||||||
|
},
|
||||||
|
canvasDimensions: {
|
||||||
|
blackframeCanvas: { // smaller than sample canvas, blackframe canvas is used to recon for black frames
|
||||||
|
// it's not used to detect aspect ratio by itself, so it can be tiny af
|
||||||
|
width: 16,
|
||||||
|
height: 9,
|
||||||
|
},
|
||||||
|
sampleCanvas: { // size of image sample for detecting aspect ratio. Bigger size means more accurate results,
|
||||||
|
// at the expense of performance
|
||||||
|
width: 640,
|
||||||
|
height: 360,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
blackLevels: {
|
blackLevels: {
|
||||||
defaultBlack: 16,
|
defaultBlack: 16,
|
||||||
blackTolerance: 4,
|
blackTolerance: 4,
|
||||||
|
|||||||
@ -152,20 +152,20 @@ export class Aard {
|
|||||||
|
|
||||||
this.canvasSamples = {
|
this.canvasSamples = {
|
||||||
top: generateSampleArray(
|
top: generateSampleArray(
|
||||||
this.settings.active.arDetect.sampling.staticCols,
|
this.settings.active.aard.sampling.staticCols,
|
||||||
this.settings.active.arDetect.canvasDimensions.sampleCanvas.width
|
this.settings.active.aard.canvasDimensions.sampleCanvas.width
|
||||||
),
|
),
|
||||||
bottom: generateSampleArray(
|
bottom: generateSampleArray(
|
||||||
this.settings.active.arDetect.sampling.staticCols,
|
this.settings.active.aard.sampling.staticCols,
|
||||||
this.settings.active.arDetect.canvasDimensions.sampleCanvas.width
|
this.settings.active.aard.canvasDimensions.sampleCanvas.width
|
||||||
),
|
),
|
||||||
left: generateSampleArray(
|
left: generateSampleArray(
|
||||||
this.settings.active.arDetect.sampling.staticCols,
|
this.settings.active.aard.sampling.staticCols,
|
||||||
this.settings.active.arDetect.canvasDimensions.sampleCanvas.height
|
this.settings.active.aard.canvasDimensions.sampleCanvas.height
|
||||||
),
|
),
|
||||||
right: generateSampleArray(
|
right: generateSampleArray(
|
||||||
this.settings.active.arDetect.sampling.staticCols,
|
this.settings.active.aard.sampling.staticCols,
|
||||||
this.settings.active.arDetect.canvasDimensions.sampleCanvas.height
|
this.settings.active.aard.canvasDimensions.sampleCanvas.height
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -188,14 +188,14 @@ export class Aard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private createCanvas(canvasId: string, canvasType?: 'webgl' | 'legacy') {
|
private createCanvas(canvasId: string, canvasType?: 'webgl' | 'legacy') {
|
||||||
ROW_SIZE = this.settings.active.arDetect.canvasDimensions.sampleCanvas.width * PIXEL_SIZE;
|
ROW_SIZE = this.settings.active.aard.canvasDimensions.sampleCanvas.width * PIXEL_SIZE;
|
||||||
|
|
||||||
if (canvasType) {
|
if (canvasType) {
|
||||||
if (canvasType === this.settings.active.arDetect.aardType || this.settings.active.arDetect.aardType === 'auto') {
|
if (canvasType === this.settings.active.aard.aardType || this.settings.active.aard.aardType === 'auto') {
|
||||||
if (canvasType === 'webgl') {
|
if (canvasType === 'webgl') {
|
||||||
return new GlCanvas({...this.settings.active.arDetect.canvasDimensions.sampleCanvas, id: 'main-gl'});
|
return new GlCanvas({...this.settings.active.aard.canvasDimensions.sampleCanvas, id: 'main-gl'});
|
||||||
} else if (canvasType === 'legacy') {
|
} else if (canvasType === 'legacy') {
|
||||||
return new FallbackCanvas({...this.settings.active.arDetect.canvasDimensions.sampleCanvas, id: 'main-legacy'});
|
return new FallbackCanvas({...this.settings.active.aard.canvasDimensions.sampleCanvas, id: 'main-legacy'});
|
||||||
} else {
|
} else {
|
||||||
// TODO: throw error
|
// TODO: throw error
|
||||||
}
|
}
|
||||||
@ -205,21 +205,21 @@ export class Aard {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['auto', 'webgl'].includes(this.settings.active.arDetect.aardType)) {
|
if (['auto', 'webgl'].includes(this.settings.active.aard.aardType)) {
|
||||||
try {
|
try {
|
||||||
return new GlCanvas({...this.settings.active.arDetect.canvasDimensions.sampleCanvas, id: 'main-gl'});
|
return new GlCanvas({...this.settings.active.aard.canvasDimensions.sampleCanvas, id: 'main-gl'});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (this.settings.active.arDetect.aardType !== 'webgl') {
|
if (this.settings.active.aard.aardType !== 'webgl') {
|
||||||
return new FallbackCanvas({...this.settings.active.arDetect.canvasDimensions.sampleCanvas, id: 'main-legacy'});
|
return new FallbackCanvas({...this.settings.active.aard.canvasDimensions.sampleCanvas, id: 'main-legacy'});
|
||||||
}
|
}
|
||||||
this.logger.error('createCanvas', 'could not create webgl canvas:', e);
|
this.logger.error('createCanvas', 'could not create webgl canvas:', e);
|
||||||
this.eventBus.send('uw-config-broadcast', {type: 'aard-error', aardErrors: {webglError: true}});
|
this.eventBus.send('uw-config-broadcast', {type: 'aard-error', aardErrors: {webglError: true}});
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
} else if (this.settings.active.arDetect.aardType === 'legacy') {
|
} else if (this.settings.active.aard.aardType === 'legacy') {
|
||||||
return new FallbackCanvas({...this.settings.active.arDetect.canvasDimensions.sampleCanvas, id: 'main-legacy'});
|
return new FallbackCanvas({...this.settings.active.aard.canvasDimensions.sampleCanvas, id: 'main-legacy'});
|
||||||
} else {
|
} else {
|
||||||
this.logger.error('createCanvas', 'invalid value in settings.arDetect.aardType:', this.settings.active.arDetect.aardType);
|
this.logger.error('createCanvas', 'invalid value in settings.arDetect.aardType:', this.settings.active.aard.aardType);
|
||||||
this.eventBus.send('uw-config-broadcast', {type: 'aard-error', aardErrors: {invalidSettings: true}});
|
this.eventBus.send('uw-config-broadcast', {type: 'aard-error', aardErrors: {invalidSettings: true}});
|
||||||
throw 'AARD_INVALID_SETTINGS';
|
throw 'AARD_INVALID_SETTINGS';
|
||||||
}
|
}
|
||||||
@ -231,7 +231,7 @@ export class Aard {
|
|||||||
*/
|
*/
|
||||||
private showDebugCanvas() {
|
private showDebugCanvas() {
|
||||||
if (!this.canvasStore.debug) {
|
if (!this.canvasStore.debug) {
|
||||||
this.canvasStore.debug = new GlDebugCanvas({...this.settings.active.arDetect.canvasDimensions.sampleCanvas, id: 'uw-debug-gl'});
|
this.canvasStore.debug = new GlDebugCanvas({...this.settings.active.aard.canvasDimensions.sampleCanvas, id: 'uw-debug-gl'});
|
||||||
}
|
}
|
||||||
this.canvasStore.debug.enableFx();
|
this.canvasStore.debug.enableFx();
|
||||||
if (!this.debugConfig.debugUi) {
|
if (!this.debugConfig.debugUi) {
|
||||||
@ -290,8 +290,8 @@ export class Aard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// do full reset of test samples
|
// do full reset of test samples
|
||||||
this.testResults = initAardTestResults(this.settings.active.arDetect);
|
this.testResults = initAardTestResults(this.settings.active.aard);
|
||||||
this.verticalTestResults = initAardTestResults(this.settings.active.arDetect);
|
this.verticalTestResults = initAardTestResults(this.settings.active.aard);
|
||||||
|
|
||||||
if (this.animationFrame) {
|
if (this.animationFrame) {
|
||||||
window.cancelAnimationFrame(this.animationFrame);
|
window.cancelAnimationFrame(this.animationFrame);
|
||||||
@ -301,8 +301,8 @@ export class Aard {
|
|||||||
this.animationFrame = window.requestAnimationFrame( (ts: DOMHighResTimeStamp) => this.onAnimationFrame(ts));
|
this.animationFrame = window.requestAnimationFrame( (ts: DOMHighResTimeStamp) => this.onAnimationFrame(ts));
|
||||||
|
|
||||||
// set auto-disable timer if detection timeout is set
|
// set auto-disable timer if detection timeout is set
|
||||||
if (this.settings.active.arDetect.autoDisable.ifNotChanged) {
|
if (this.settings.active.aard.autoDisable.ifNotChanged) {
|
||||||
this.timers.autoDisableAt = Date.now() + this.settings.active.arDetect.autoDisable.ifNotChangedTimeout;
|
this.timers.autoDisableAt = Date.now() + this.settings.active.aard.autoDisable.ifNotChangedTimeout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,8 +314,8 @@ export class Aard {
|
|||||||
this.stop();
|
this.stop();
|
||||||
|
|
||||||
if (options?.noCache) {
|
if (options?.noCache) {
|
||||||
this.testResults = initAardTestResults(this.settings.active.arDetect);
|
this.testResults = initAardTestResults(this.settings.active.aard);
|
||||||
this.verticalTestResults = initAardTestResults(this.settings.active.arDetect);
|
this.verticalTestResults = initAardTestResults(this.settings.active.aard);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.main();
|
this.main();
|
||||||
@ -342,7 +342,7 @@ export class Aard {
|
|||||||
// if video was paused & we know that we already checked that frame,
|
// if video was paused & we know that we already checked that frame,
|
||||||
// we will not check it again.
|
// we will not check it again.
|
||||||
const videoState = this.getVideoPlaybackState();
|
const videoState = this.getVideoPlaybackState();
|
||||||
const polling = this.settings.active.arDetect.polling;
|
const polling = this.settings.active.aard.polling;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
if (videoState !== VideoPlaybackState.Playing) {
|
if (videoState !== VideoPlaybackState.Playing) {
|
||||||
@ -375,8 +375,8 @@ export class Aard {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.timers.nextFrameCheckTime = now + this.settings.active.arDetect.timers.playing;
|
this.timers.nextFrameCheckTime = now + this.settings.active.aard.timers.playing;
|
||||||
this.timers.reducedPollingNextCheckTime = now + this.settings.active.arDetect.timers.playingReduced;
|
this.timers.reducedPollingNextCheckTime = now + this.settings.active.aard.timers.playingReduced;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,7 +403,7 @@ export class Aard {
|
|||||||
* Main loop for scanning aspect ratio changes
|
* Main loop for scanning aspect ratio changes
|
||||||
*/
|
*/
|
||||||
private async main() {
|
private async main() {
|
||||||
const arConf = this.settings.active.arDetect;
|
const arConf = this.settings.active.aard;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.timer.next();
|
this.timer.next();
|
||||||
@ -745,8 +745,8 @@ export class Aard {
|
|||||||
const lastPixelOffset = ROW_SIZE - PIXEL_SIZE;
|
const lastPixelOffset = ROW_SIZE - PIXEL_SIZE;
|
||||||
const imageSize = ROW_SIZE * height;
|
const imageSize = ROW_SIZE * height;
|
||||||
|
|
||||||
const xLimit = this.settings.active.arDetect.letterboxOrientationScan.letterboxLimit;
|
const xLimit = this.settings.active.aard.letterboxOrientationScan.letterboxLimit;
|
||||||
const yLimit = this.settings.active.arDetect.letterboxOrientationScan.pillarboxLimit;
|
const yLimit = this.settings.active.aard.letterboxOrientationScan.pillarboxLimit;
|
||||||
|
|
||||||
let letterbox = true, pillarbox = true;
|
let letterbox = true, pillarbox = true;
|
||||||
let xCount = 0, yCount = 0;
|
let xCount = 0, yCount = 0;
|
||||||
@ -844,7 +844,7 @@ export class Aard {
|
|||||||
*/
|
*/
|
||||||
private updateLetterboxEdgeCandidates(crossDimension: number, topCandidate: number, bottomCandidate: number) {
|
private updateLetterboxEdgeCandidates(crossDimension: number, topCandidate: number, bottomCandidate: number) {
|
||||||
const bottomDistance = (crossDimension - bottomCandidate);
|
const bottomDistance = (crossDimension - bottomCandidate);
|
||||||
const maxOffset = ~~(crossDimension * this.settings.active.arDetect.edgeDetection.maxLetterboxOffset);
|
const maxOffset = ~~(crossDimension * this.settings.active.aard.edgeDetection.maxLetterboxOffset);
|
||||||
const diff = Math.abs(topCandidate - bottomDistance);
|
const diff = Math.abs(topCandidate - bottomDistance);
|
||||||
const candidateAvg = ~~((topCandidate + bottomDistance) * 0.5);
|
const candidateAvg = ~~((topCandidate + bottomDistance) * 0.5);
|
||||||
|
|
||||||
@ -869,7 +869,7 @@ export class Aard {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
private subtitleScan(imageData: Uint8Array, width: number, height: number, skipAdvancedScan: boolean) {
|
private subtitleScan(imageData: Uint8Array, width: number, height: number, skipAdvancedScan: boolean) {
|
||||||
const scanConf = this.settings.active.arDetect.subtitles;
|
const scanConf = this.settings.active.aard.subtitles;
|
||||||
|
|
||||||
this.testResults.subtitleDetected = false;
|
this.testResults.subtitleDetected = false;
|
||||||
|
|
||||||
@ -931,8 +931,8 @@ export class Aard {
|
|||||||
) {
|
) {
|
||||||
results.uncertain = false;
|
results.uncertain = false;
|
||||||
|
|
||||||
const scanConf = this.settings.active.arDetect.subtitles;
|
const scanConf = this.settings.active.aard.subtitles;
|
||||||
const arConf = this.settings.active.arDetect;
|
const arConf = this.settings.active.aard;
|
||||||
|
|
||||||
let letterCount, imageSegmentCount, potentialFadedLetterCount, potentialFadedLetterCountInvalidated, nonGradientPixelCount, letterSize, imageSize, imageSegmentSize, imageWeightedSize, segmentWeights, imageSegmentAlignment, imageSegmentAlignmentSamples,
|
let letterCount, imageSegmentCount, potentialFadedLetterCount, potentialFadedLetterCountInvalidated, nonGradientPixelCount, letterSize, imageSize, imageSegmentSize, imageWeightedSize, segmentWeights, imageSegmentAlignment, imageSegmentAlignmentSamples,
|
||||||
isOnLetter, isOnImage, isBlank,
|
isOnLetter, isOnImage, isBlank,
|
||||||
@ -940,7 +940,7 @@ export class Aard {
|
|||||||
let rowStart, rowEnd, rowMid, rowGTA, rowGTB; // GT = gradient test
|
let rowStart, rowEnd, rowMid, rowGTA, rowGTB; // GT = gradient test
|
||||||
let imageConfirmPass = false, subtitleConfirmPass = false;
|
let imageConfirmPass = false, subtitleConfirmPass = false;
|
||||||
|
|
||||||
let innerIteration, outerIteration = 0;
|
let outerIteration = 0;
|
||||||
|
|
||||||
const rowMargin = Math.floor(scanConf.scanMargin * ROW_SIZE);
|
const rowMargin = Math.floor(scanConf.scanMargin * ROW_SIZE);
|
||||||
const imageThreshold = Math.floor((ROW_SIZE - (rowMargin * 2)) * arConf.edgeDetection.minValidImage * PIXEL_SIZE_FRACTION);
|
const imageThreshold = Math.floor((ROW_SIZE - (rowMargin * 2)) * arConf.edgeDetection.minValidImage * PIXEL_SIZE_FRACTION);
|
||||||
@ -953,8 +953,11 @@ export class Aard {
|
|||||||
(scanSpacing > 0 && searchRow < endRow) || (scanSpacing < 0 && searchRow > endRow);
|
(scanSpacing > 0 && searchRow < endRow) || (scanSpacing < 0 && searchRow > endRow);
|
||||||
searchRow += scanSpacing
|
searchRow += scanSpacing
|
||||||
) {
|
) {
|
||||||
innerIteration = 0;
|
if (++outerIteration > height) {
|
||||||
outerIteration++;
|
console.warn('[ultrawidify|aard::subtitleScanRegionLinear] — scan got stuck in an infinite loop. This shouldn\'t happen.');
|
||||||
|
results.uncertain;
|
||||||
|
break outerLoop;
|
||||||
|
}
|
||||||
|
|
||||||
letterCount = 0;
|
letterCount = 0;
|
||||||
potentialFadedLetterCount = 0;
|
potentialFadedLetterCount = 0;
|
||||||
@ -1019,8 +1022,6 @@ export class Aard {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
while (rowStart < rowEnd) {
|
while (rowStart < rowEnd) {
|
||||||
innerIteration++;
|
|
||||||
|
|
||||||
const r = imageData[rowStart], g = imageData[rowStart + 1], b = imageData[rowStart + 2];
|
const r = imageData[rowStart], g = imageData[rowStart + 1], b = imageData[rowStart + 2];
|
||||||
|
|
||||||
const on = r > scanConf.subtitleSubpixelThresholdOn
|
const on = r > scanConf.subtitleSubpixelThresholdOn
|
||||||
@ -1316,7 +1317,7 @@ export class Aard {
|
|||||||
const maxRatio = Math.max(ar, this.testResults.activeAspectRatio);
|
const maxRatio = Math.max(ar, this.testResults.activeAspectRatio);
|
||||||
const diff = Math.abs(ar - this.testResults.activeAspectRatio);
|
const diff = Math.abs(ar - this.testResults.activeAspectRatio);
|
||||||
|
|
||||||
if ((diff / maxRatio) > this.settings.active.arDetect.allowedArVariance || options?.forceReset) {
|
if ((diff / maxRatio) > this.settings.active.aard.allowedArVariance || options?.forceReset) {
|
||||||
this.videoData.resizer.updateAr({
|
this.videoData.resizer.updateAr({
|
||||||
type: AspectRatioType.AutomaticUpdate,
|
type: AspectRatioType.AutomaticUpdate,
|
||||||
ratio: ar,
|
ratio: ar,
|
||||||
@ -1326,11 +1327,11 @@ export class Aard {
|
|||||||
this.testResults.activeAspectRatio = ar;
|
this.testResults.activeAspectRatio = ar;
|
||||||
|
|
||||||
if (!options?.uncertainDetection) {
|
if (!options?.uncertainDetection) {
|
||||||
if (this.settings.active.arDetect.autoDisable.onFirstChange) {
|
if (this.settings.active.aard.autoDisable.onFirstChange) {
|
||||||
this.status.autoDisabled = true;
|
this.status.autoDisabled = true;
|
||||||
}
|
}
|
||||||
if (this.settings.active.arDetect.autoDisable.ifNotChanged) {
|
if (this.settings.active.aard.autoDisable.ifNotChanged) {
|
||||||
this.timers.autoDisableAt = Date.now() + this.settings.active.arDetect.autoDisable.ifNotChangedTimeout;
|
this.timers.autoDisableAt = Date.now() + this.settings.active.aard.autoDisable.ifNotChangedTimeout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
|
import { Aard } from './Aard';
|
||||||
|
import { AardLegacy } from './AardLegacy';
|
||||||
import { AardPerformanceData } from './AardTimers';
|
import { AardPerformanceData } from './AardTimers';
|
||||||
|
import { FallbackCanvas } from './gl/FallbackCanvas';
|
||||||
|
|
||||||
export class AardDebugUi {
|
export class AardDebugUi {
|
||||||
|
|
||||||
@ -76,37 +79,11 @@ export class AardDebugUi {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div id="uw-aard-debug-ui_body" style="display: flex; flex-direction: row; width: 100%">
|
<div id="uw-aard-debug-ui_body" style="display: flex; flex-direction: row; width: 100%; margin-top: 8rem;">
|
||||||
<div style="">
|
<div style="">
|
||||||
<div id="uw-aard-debug_aard-sample-canvas" style="min-width: 640px"></div>
|
<div id="uw-aard-debug_aard-sample-canvas" style="min-width: 640px"></div>
|
||||||
<div style="background: black; color: #fff"; font-size: 24px;">AARD IN</div>
|
<div style="background: black; color: #fff"; font-size: 24px;">AARD IN</div>
|
||||||
|
|
||||||
<div style="background: black; color: #ccc; padding: 1rem">
|
|
||||||
<div>
|
|
||||||
<span style="color: rgb(0.1, 0.1, 0.35)">■</span>
|
|
||||||
Black level sample
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span style="color: rgb(0.3, 1.0, 0.6)">■</span>
|
|
||||||
<span style="color: rgb(0.1, 0.5, 0.3)">■</span>
|
|
||||||
Guard line (middle/corner) OK
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span style="color: rgb(1.0, 0.1, 0.1)">■</span>
|
|
||||||
<span style="color: rgb(0.5, 0.0, 0.0)">■</span>
|
|
||||||
Guard line (middle/corner) violation
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Image line — <span style="color: rgb(0.7, 0.7, 0.7)">■</span> image, <span style="color: rgb(0.2, 0.2, 0.6)">■</span> no image
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Edge scan — <span style="color: rgb(0.1, 0.1, 0.4)">■</span> probe, <span style="color: rgb(0.4, 0.4, 1.0)">■</span> hit
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Slope test — <span style="color: rgb(0.4, 0.4, 1.0)">■</span> ok, <span style="color: rgb(1.0, 0.0, 0.0)">■</span> fail
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="pointer-events: all">
|
<div style="pointer-events: all">
|
||||||
<button id="uw-aard-debug-ui_enable-stop-on-change" style="">Pause video on aspect ratio change</button>
|
<button id="uw-aard-debug-ui_enable-stop-on-change" style="">Pause video on aspect ratio change</button>
|
||||||
<button id="uw-aard-debug-ui_disable-stop-on-change" style="display: none">Stop pausing video on aspect ratio change</button>
|
<button id="uw-aard-debug-ui_disable-stop-on-change" style="display: none">Stop pausing video on aspect ratio change</button>
|
||||||
@ -125,10 +102,9 @@ export class AardDebugUi {
|
|||||||
<pre id="uw-aard-results"></pre>
|
<pre id="uw-aard-results"></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="width: 1920px">
|
<div style="width: 1920px; border: 2px dotted #142; margin-right:2rem;">
|
||||||
<div id="uw-aard-debug_aard-output" style="zoom: 3; image-rendering: pixelated;"></div>
|
<div id="uw-aard-debug_aard-output" style="zoom: 3; image-rendering: pixelated;"></div>
|
||||||
<div style="background: black; color: #fff; font-size: 24px;">AARD RESULT</div>
|
<div style="background: black; color: #fff; font-size: 24px;">AARD RESULT</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -194,9 +170,19 @@ export class AardDebugUi {
|
|||||||
|
|
||||||
const popupContent = `
|
const popupContent = `
|
||||||
<h2 style="color: #fa6; margin-bottom: 1rem">Detailed performance analysis:</h2>
|
<h2 style="color: #fa6; margin-bottom: 1rem">Detailed performance analysis:</h2>
|
||||||
|
<div>
|
||||||
|
<span style="color: #fff">About:</span><br/>
|
||||||
|
Aard version: <span style="color: #fa6">${this.aard instanceof AardLegacy ? 'legacy' : this.aard instanceof Aard ? 'experimental' : 'unknown'}</span> <small>(instanceof AardLegacy? ${this.aard instanceof AardLegacy}, Aard? ${this.aard instanceof Aard})</small><br/>
|
||||||
|
canvas type: <span style="color: #fa6">${!this.aard.canvasStore.main ? '<canvas not initialized>' : this.aard.canvasStore.main instanceof FallbackCanvas ? '2dCanvas' : 'webgl'}</span> |
|
||||||
|
legacy default: <span style="color: #fa6">${this.aard.settings.active.aardLegacy.aardType}</span>,
|
||||||
|
experimental default: <span style="color: #fa6">${this.aard.settings.active.aard.aardType}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
<div style="width: 100%; display: flex; flex-direction: column">
|
<div style="width: 100%; display: flex; flex-direction: column">
|
||||||
<div style="margin-bottom: 1rem;">
|
<div style="margin-bottom: 1rem;">
|
||||||
${this.generateRawTimes(this.aard.timer.average)}
|
${this.generateRawTimes(this.aard.timer.current)}
|
||||||
</div>
|
</div>
|
||||||
<div style="color: #fff">Stage times (not cumulative):</div>
|
<div style="color: #fff">Stage times (not cumulative):</div>
|
||||||
<div style="display: flex; flex-direction: row; width: 100%; height: 150px">
|
<div style="display: flex; flex-direction: row; width: 100%; height: 150px">
|
||||||
@ -214,12 +200,14 @@ export class AardDebugUi {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="margin-bottom: 1rem;">
|
<div style="margin-bottom: 1rem;">
|
||||||
${this.generateRawTimes(this.aard.timer.average)}
|
${this.generateRawTimes(this.aard.timer.lastChange)}
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex; flex-direction: row; width: 100%; height: 150px">
|
<div style="display: flex; flex-direction: row; width: 100%; height: 150px">
|
||||||
<div style="width: 160px; text-align: right; padding-right: 4px;">Last change:</div>
|
<div style="width: 160px; text-align: right; padding-right: 4px;">Last change:</div>
|
||||||
<div style="flex-grow: 1;">${this.generateMiniGraphBar(this.aard.timer.lastChange, true)}</div>
|
<div style="flex-grow: 1;">${this.generateMiniGraphBar(this.aard.timer.lastChange, true)}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- <pre>${JSON.stringify({current: this.aard.timer.current, average: this.aard.timer.average, lastChange: this.aard.timer.lastChange}, null, 2)}</pre> -->
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -298,23 +286,23 @@ export class AardDebugUi {
|
|||||||
total += fastBlackLevel;
|
total += fastBlackLevel;
|
||||||
|
|
||||||
const guardLineStart = fastBlackLevelStart + fastBlackLevel;
|
const guardLineStart = fastBlackLevelStart + fastBlackLevel;
|
||||||
const guardLine = Math.max(perf.guardLine - total, 0);
|
const guardLine = (perf.guardLine !== undefined && perf.guardLine !== -1) ? Math.max(perf.guardLine - total, 0) : 0;
|
||||||
total += guardLine;
|
total += guardLine;
|
||||||
|
|
||||||
const edgeScanStart = guardLineStart + guardLine;
|
const edgeScanStart = guardLineStart + guardLine;
|
||||||
const edgeScan = Math.max(perf.edgeScan - total, 0);
|
const edgeScan = (perf.edgeScan !== undefined && perf.edgeScan !== -1) ? Math.max(perf.edgeScan - total, 0) : 0;
|
||||||
total += edgeScan;
|
total += edgeScan;
|
||||||
|
|
||||||
const gradientStart = edgeScanStart + edgeScan;
|
const gradientStart = edgeScanStart + edgeScan;
|
||||||
const gradient = Math.max(perf.gradient - total, 0);
|
const gradient = (perf.gradient !== undefined && perf.gradient !== -1) ? Math.max(perf.gradient - total, 0) : 0;
|
||||||
total += gradient;
|
total += gradient;
|
||||||
|
|
||||||
const scanResultsStart = gradientStart + gradient;
|
const scanResultsStart = gradientStart + gradient;
|
||||||
const scanResults = Math.max(perf.scanResults - total, 0);
|
const scanResults = (perf.scanResults !== undefined && perf.scanResults !== -1) ? Math.max(perf.scanResults - total, 0) : 0;
|
||||||
total += scanResults;
|
total += scanResults;
|
||||||
|
|
||||||
const subtitleScanStart = scanResultsStart + scanResults;
|
const subtitleScanStart = scanResultsStart + scanResults;
|
||||||
const subtitleScan = Math.max(perf.scanResults - total, 0);
|
const subtitleScan = Math.max(perf.subtitleScan - total, 0);
|
||||||
total += subtitleScan;
|
total += subtitleScan;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
@ -344,12 +332,10 @@ export class AardDebugUi {
|
|||||||
<div style="position: absolute; top: ${detailed ? '74px' : '2px'}; left: ${scanResultsStart}%; min-width: 1px; width: ${scanResults}%; background: #80f; height: 12px;"></div>
|
<div style="position: absolute; top: ${detailed ? '74px' : '2px'}; left: ${scanResultsStart}%; min-width: 1px; width: ${scanResults}%; background: #80f; height: 12px;"></div>
|
||||||
${this.getBarLabel(scanResults, scanResultsStart, 74, `scan results processing: ${scanResults.toFixed(2)} ms`, detailed)}
|
${this.getBarLabel(scanResults, scanResultsStart, 74, `scan results processing: ${scanResults.toFixed(2)} ms`, detailed)}
|
||||||
|
|
||||||
${this.getBarLabel(0, scanResults + scanResultsStart, 88, `total: ${total.toFixed(2)} ms`, detailed, 'color: #fff;')}
|
<div style="position: absolute; top: ${detailed ? '86px' : '2px'}; left: ${subtitleScanStart}%; min-width: 1px; width: ${subtitleScan}%; background: rgba(234, 204, 84, 1); height: 12px;"></div>
|
||||||
|
${this.getBarLabel(subtitleScan, subtitleScanStart, 86, `subtitle scan: ${subtitleScan.toFixed(2)} ms`, detailed)}
|
||||||
|
|
||||||
<div style="position: absolute; top: ${detailed ? '74px' : '2px'}; left: ${subtitleScanStart}%; min-width: 1px; width: ${subtitleScan}%; background: rgba(234, 204, 84, 1); height: 12px;"></div>
|
${this.getBarLabel(0, subtitleScan + subtitleScanStart, 98, `total: ${total.toFixed(2)} ms`, detailed, 'color: #fff;')}
|
||||||
${this.getBarLabel(scanResults, scanResultsStart, 74, `scan results processing: ${subtitleScan.toFixed(2)} ms`, detailed)}
|
|
||||||
|
|
||||||
${this.getBarLabel(0, scanResults + scanResultsStart, 88, `total: ${total.toFixed(2)} ms`, detailed, 'color: #fff;')}
|
|
||||||
|
|
||||||
<!-- 60/30 fps markers -->
|
<!-- 60/30 fps markers -->
|
||||||
<div style="position: absolute; top: ${detailed ? '-12px' : '0'}; left: 16.666%; width: 1px; border-left: 1px dashed #4f9; height: ${detailed ? '112px' : '12px'}; padding-left: 2px; background-color: rgba(0,0,0,0.5); z-index: ${detailed ? '5' : '2'}000;">60fps</div>
|
<div style="position: absolute; top: ${detailed ? '-12px' : '0'}; left: 16.666%; width: 1px; border-left: 1px dashed #4f9; height: ${detailed ? '112px' : '12px'}; padding-left: 2px; background-color: rgba(0,0,0,0.5); z-index: ${detailed ? '5' : '2'}000;">60fps</div>
|
||||||
@ -359,7 +345,7 @@ export class AardDebugUi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_lastAr: undefined;
|
_lastAr: undefined;
|
||||||
updateTestResults(testResults) {
|
updateTestResults(testResults, timers) {
|
||||||
this.updatePerformanceResults();
|
this.updatePerformanceResults();
|
||||||
|
|
||||||
if (testResults.aspectRatioUpdated && this.pauseOnArCheck) {
|
if (testResults.aspectRatioUpdated && this.pauseOnArCheck) {
|
||||||
@ -378,52 +364,13 @@ export class AardDebugUi {
|
|||||||
Active: ${ar}, changed since last check? ${testResults.aspectRatioUpdated} letterbox width: ${testResults.letterboxWidth} offset ${testResults.letterboxOffset}<br/>
|
Active: ${ar}, changed since last check? ${testResults.aspectRatioUpdated} letterbox width: ${testResults.letterboxWidth} offset ${testResults.letterboxOffset}<br/>
|
||||||
<sup>(last: ${this._lastAr})</sup>
|
<sup>(last: ${this._lastAr})</sup>
|
||||||
|
|
||||||
|
Paused until? ${Date.now() < timers.pauseUntil ? ((timers.pauseUntil - Date.now()) / 1000) + 's' : 'not paused'};
|
||||||
|
<sup>now: ${Date.now()}ms; until:${timers.pauseUntil}ms; diff: ${(+timers.pauseUntil - Date.now())} </sup>
|
||||||
|
|
||||||
image in black level probe (aka "not letterbox"): ${testResults.notLetterbox}
|
image in black level probe (aka "not letterbox"): ${testResults.notLetterbox}
|
||||||
`;
|
`;
|
||||||
this._lastAr = ar;
|
this._lastAr = ar;
|
||||||
|
|
||||||
if (testResults.notLetterbox) {
|
|
||||||
resultsDiv.innerHTML = out;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
out = `${out}
|
|
||||||
|
|
||||||
-- UNCERTAIN FLAGS
|
|
||||||
AR: ${testResults.aspectRatioUncertain} (reason: ${testResults.aspectRatioUncertainReason ?? 'n/a'}); top row: ${testResults.topRowUncertain}; bottom row: ${testResults.bottomRowUncertain}${
|
|
||||||
testResults.aspectRatioInvalid ? `\nINVALID_AR (reason: ${testResults.aspectRatioInvalidReason ?? 'n/a'})` : ''}
|
|
||||||
|
|
||||||
-- GUARD & IMAGE LINE
|
|
||||||
bottom guard: ${testResults.guardLine.bottom} image: ${testResults.guardLine.invalidated ? 'n/a' : testResults.imageLine.bottom}
|
|
||||||
top guard: ${testResults.guardLine.top} image: ${testResults.guardLine.invalidated ? 'n/a' : testResults.imageLine.top}
|
|
||||||
|
|
||||||
guard line ${testResults.guardLine.invalidated ? 'INVALIDATED' : 'valid'} image line ${testResults.guardLine.invalidated ? '<skipped test>' : testResults.imageLine.invalidated ? 'INVALIDATED' : 'valid'}
|
|
||||||
|
|
||||||
corner invalidations (invalid pixels -> verdict)
|
|
||||||
|
|
||||||
LEFT CENTER RIGHT
|
|
||||||
bottom: ${testResults.guardLine.cornerPixelsViolated[0]} → ${testResults.guardLine.cornerViolated[0] ? '❌' : '◽'} ${testResults.guardLine.cornerPixelsViolated[1]} → ${testResults.guardLine.cornerViolated[1] ? '❌' : '◽'}
|
|
||||||
top: ${testResults.guardLine.cornerPixelsViolated[2]} → ${testResults.guardLine.cornerViolated[2] ? '❌' : '◽'} ${testResults.guardLine.cornerPixelsViolated[3]} → ${testResults.guardLine.cornerViolated[3] ? '❌' : '◽'}
|
|
||||||
|
|
||||||
-- AR SCAN ${testResults.lastStage < 1 ? `
|
|
||||||
DID NOT RUN THIS FRAME` : `
|
|
||||||
|
|
||||||
LEFT CENTER RIGHT CANDIDATE
|
|
||||||
BOTTOM
|
|
||||||
distance: ${testResults.aspectRatioCheck.bottomRows[0]} ${testResults.aspectRatioCheck.bottomRows[1]} ${testResults.aspectRatioCheck.bottomRows[2]} ${testResults.aspectRatioCheck.bottomCandidate}
|
|
||||||
quality: ${testResults.aspectRatioCheck.bottomQuality[0]} ${testResults.aspectRatioCheck.bottomQuality[1]} ${testResults.aspectRatioCheck.bottomQuality[2]} ${testResults.aspectRatioCheck.bottomCandidateQuality}
|
|
||||||
|
|
||||||
TOP
|
|
||||||
distance: ${testResults.aspectRatioCheck.topRows[0]} ${testResults.aspectRatioCheck.topRows[1]} ${testResults.aspectRatioCheck.topRows[2]} ${testResults.aspectRatioCheck.topCandidate}
|
|
||||||
quality: ${testResults.aspectRatioCheck.topQuality[0]} ${testResults.aspectRatioCheck.topQuality[1]} ${testResults.aspectRatioCheck.topQuality[2]} ${testResults.aspectRatioCheck.topCandidateQuality}
|
|
||||||
|
|
||||||
Diff matrix:
|
|
||||||
R-L C-R C-L
|
|
||||||
bottom: ${testResults.aspectRatioCheck.bottomRowsDifferenceMatrix[0]} ${testResults.aspectRatioCheck.bottomRowsDifferenceMatrix[1]} ${testResults.aspectRatioCheck.bottomRowsDifferenceMatrix[2]}
|
|
||||||
top: ${testResults.aspectRatioCheck.topRowsDifferenceMatrix[0]} ${testResults.aspectRatioCheck.topRowsDifferenceMatrix[1]} ${testResults.aspectRatioCheck.topRowsDifferenceMatrix[2]}
|
|
||||||
`}
|
|
||||||
`;
|
|
||||||
|
|
||||||
resultsDiv.innerHTML = out;
|
resultsDiv.innerHTML = out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2144
src/ext/lib/aard/AardLegacy.ts
Normal file
2144
src/ext/lib/aard/AardLegacy.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,11 +4,13 @@ export interface AardPerformanceData {
|
|||||||
getImage: number;
|
getImage: number;
|
||||||
|
|
||||||
fastBlackLevel: number;
|
fastBlackLevel: number;
|
||||||
guardLine: number; // actually times both guard line and image line checks
|
|
||||||
edgeScan: number; // includes validation step
|
// optional ones are only available in legacy aard
|
||||||
gradient: number;
|
guardLine?: number; // actually times both guard line and image line checks
|
||||||
|
edgeScan?: number; // includes validation step
|
||||||
|
gradient?: number;
|
||||||
|
scanResults?: number;
|
||||||
subtitleScan: number;
|
subtitleScan: number;
|
||||||
scanResults: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -82,36 +84,52 @@ export class AardTimer {
|
|||||||
getAverage() {
|
getAverage() {
|
||||||
for (let i = 0; i < this.aardPerformanceDataBuffer.length; i++) {
|
for (let i = 0; i < this.aardPerformanceDataBuffer.length; i++) {
|
||||||
const sample = this.aardPerformanceDataBuffer[i];
|
const sample = this.aardPerformanceDataBuffer[i];
|
||||||
|
|
||||||
if (sample.draw !== -1) {
|
if (sample.draw !== -1) {
|
||||||
this.average.draw += sample.draw;
|
this.average.draw += sample.draw;
|
||||||
}
|
}
|
||||||
if (sample.getImage !== -1) {
|
if (sample.getImage !== -1) {
|
||||||
this.average.getImage += sample.getImage;
|
this.average.getImage += sample.getImage;
|
||||||
}
|
}
|
||||||
if (sample.fastBlackLevel !== -1) {
|
if (sample.fastBlackLevel !== -1 && this.average.fastBlackLevel !== null) {
|
||||||
this.average.fastBlackLevel += sample.fastBlackLevel;
|
this.average.fastBlackLevel += sample.fastBlackLevel;
|
||||||
|
} else {
|
||||||
|
this.average.fastBlackLevel = null;
|
||||||
}
|
}
|
||||||
if (sample.guardLine !== -1) {
|
if (sample.guardLine !== -1 && this.average.guardLine !== null) {
|
||||||
this.average.guardLine += sample.guardLine;
|
this.average.guardLine += sample.guardLine;
|
||||||
|
} else {
|
||||||
|
this.average.guardLine = null;
|
||||||
}
|
}
|
||||||
if (sample.edgeScan !== -1) {
|
if (sample.edgeScan !== -1 && this.average.edgeScan !== null) {
|
||||||
this.average.edgeScan += sample.edgeScan;
|
this.average.edgeScan += sample.edgeScan;
|
||||||
|
} else {
|
||||||
|
this.average.edgeScan = null;
|
||||||
}
|
}
|
||||||
if (sample.gradient !== -1) {
|
if (sample.gradient !== -1 && this.average.gradient !== null) {
|
||||||
this.average.gradient += sample.gradient;
|
this.average.gradient += sample.gradient;
|
||||||
|
} else {
|
||||||
|
this.average.edgeScan = null;
|
||||||
}
|
}
|
||||||
if (sample.scanResults !== -1) {
|
if (sample.scanResults !== -1 && this.average.scanResults !== null) {
|
||||||
this.average.scanResults += sample.scanResults;
|
this.average.scanResults += sample.scanResults;
|
||||||
|
} else {
|
||||||
|
this.average.scanResults = null;
|
||||||
|
}
|
||||||
|
if (sample.subtitleScan !== -1 && this.average.subtitleScan !== null) {
|
||||||
|
this.average.subtitleScan += sample.subtitleScan;
|
||||||
|
} else {
|
||||||
|
this.average.subtitleScan = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.average.draw /= this.aardPerformanceDataBuffer.length;
|
this.average.draw /= this.aardPerformanceDataBuffer.length;
|
||||||
this.average.getImage /= this.aardPerformanceDataBuffer.length;
|
this.average.getImage /= this.aardPerformanceDataBuffer.length;
|
||||||
this.average.fastBlackLevel /= this.aardPerformanceDataBuffer.length;
|
this.average.fastBlackLevel /= this.aardPerformanceDataBuffer.length;
|
||||||
this.average.guardLine /= this.aardPerformanceDataBuffer.length;
|
this.average.guardLine = this.average.guardLine === null ? -1 : (this.average.guardLine / this.aardPerformanceDataBuffer.length);
|
||||||
this.average.edgeScan /= this.aardPerformanceDataBuffer.length;
|
this.average.edgeScan = this.average.edgeScan === null ? -1 : (this.average.guardLine / this.aardPerformanceDataBuffer.length);
|
||||||
this.average.gradient /= this.aardPerformanceDataBuffer.length;
|
this.average.gradient = this.average.gradient === null ? -1 : (this.average.guardLine / this.aardPerformanceDataBuffer.length);
|
||||||
this.average.scanResults /= this.aardPerformanceDataBuffer.length;
|
this.average.scanResults = this.average.scanResults === null ? -1 : (this.average.guardLine / this.aardPerformanceDataBuffer.length);
|
||||||
this.average.subtitleScan /= this.aardPerformanceDataBuffer.length;
|
this.average.subtitleScan = this.average.subtitleScan === null ? -1 : (this.average.subtitleScan /this.aardPerformanceDataBuffer.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,189 @@
|
|||||||
|
import { AardLegacySettings, AardSettings } from '../../../../common/interfaces/SettingsInterface'
|
||||||
|
import { AardUncertainReason } from '../enums/aard-letterbox-uncertain-reason.enum'
|
||||||
|
import { LetterboxOrientation } from '../enums/letterbox-orientation.enum'
|
||||||
|
|
||||||
|
export interface AardTestResult_SubtitleRegion {
|
||||||
|
firstBlank: number,
|
||||||
|
lastBlank: number,
|
||||||
|
firstSubtitle: number,
|
||||||
|
lastSubtitle: number,
|
||||||
|
firstImage: number,
|
||||||
|
lastImage: number,
|
||||||
|
uncertain: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AardLegacyTestResults {
|
||||||
|
isFinished: boolean,
|
||||||
|
lastStage: number,
|
||||||
|
letterboxOrientation: LetterboxOrientation,
|
||||||
|
lastValidLetterboxOrientation: LetterboxOrientation,
|
||||||
|
subtitleDetected: boolean,
|
||||||
|
blackLevel: number, // is cumulative
|
||||||
|
blackThreshold: number, // is cumulative
|
||||||
|
guardLine: {
|
||||||
|
top: number, // is cumulative
|
||||||
|
bottom: number, // is cumulative
|
||||||
|
invalidated: boolean,
|
||||||
|
cornerViolated: [boolean, boolean, boolean, boolean],
|
||||||
|
cornerPixelsViolated: [0,0,0,0],
|
||||||
|
front?: number,
|
||||||
|
back?: number,
|
||||||
|
},
|
||||||
|
imageLine: {
|
||||||
|
top: number, // is cumulative
|
||||||
|
bottom: number, // is cumulative
|
||||||
|
invalidated: boolean
|
||||||
|
}
|
||||||
|
aspectRatioCheck: {
|
||||||
|
topRows: [number, number, number],
|
||||||
|
topQuality: [number, number, number],
|
||||||
|
bottomRows: [number, number, number],
|
||||||
|
bottomQuality: [number, number, number],
|
||||||
|
topCandidate: number,
|
||||||
|
topCandidateQuality: number,
|
||||||
|
bottomCandidate: number,
|
||||||
|
bottomCandidateDistance: number,
|
||||||
|
bottomCandidateQuality: number,
|
||||||
|
topRowsDifferenceMatrix: [number, number, number],
|
||||||
|
bottomRowsDifferenceMatrix: [number, number, number],
|
||||||
|
|
||||||
|
frontCandidate: number,
|
||||||
|
backCandidate: number,
|
||||||
|
},
|
||||||
|
aspectRatioUncertain: boolean,
|
||||||
|
topRowUncertain: boolean,
|
||||||
|
bottomRowUncertain: boolean,
|
||||||
|
aspectRatioUpdated: boolean,
|
||||||
|
activeAspectRatio: number, // is cumulative
|
||||||
|
letterboxSize: number,
|
||||||
|
letterboxOffset: number,
|
||||||
|
logoDetected: [boolean, boolean, boolean, boolean],
|
||||||
|
aspectRatioInvalid: boolean,
|
||||||
|
subtitleScan: {
|
||||||
|
top: number,
|
||||||
|
bottom: number,
|
||||||
|
|
||||||
|
regions: {
|
||||||
|
top: AardTestResult_SubtitleRegion,
|
||||||
|
bottom: AardTestResult_SubtitleRegion
|
||||||
|
}
|
||||||
|
},
|
||||||
|
notLetterbox: boolean,
|
||||||
|
aspectRatioUncertainEdges: number,
|
||||||
|
aspectRatioUncertainReason?: AardUncertainReason,
|
||||||
|
aspectRatioInvalidReason?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initAardTestResults(settings: AardLegacySettings): AardLegacyTestResults {
|
||||||
|
return {
|
||||||
|
isFinished: true,
|
||||||
|
lastStage: 0,
|
||||||
|
letterboxOrientation: LetterboxOrientation.NotKnown,
|
||||||
|
lastValidLetterboxOrientation: LetterboxOrientation.NotKnown,
|
||||||
|
blackLevel: settings.blackLevels.defaultBlack,
|
||||||
|
blackThreshold: 16,
|
||||||
|
guardLine: {
|
||||||
|
top: -1,
|
||||||
|
bottom: -1,
|
||||||
|
invalidated: false,
|
||||||
|
cornerViolated: [false, false, false, false],
|
||||||
|
cornerPixelsViolated: [0,0,0,0],
|
||||||
|
|
||||||
|
front: -1,
|
||||||
|
back: -1,
|
||||||
|
},
|
||||||
|
imageLine: {
|
||||||
|
top: -1,
|
||||||
|
bottom: -1,
|
||||||
|
invalidated: false,
|
||||||
|
},
|
||||||
|
aspectRatioCheck: {
|
||||||
|
topRows: [-1, -1, -1],
|
||||||
|
topQuality: [0, 0, 0],
|
||||||
|
bottomRows: [-1, -1, -1],
|
||||||
|
bottomQuality: [0, 0, 0],
|
||||||
|
topCandidate: 0,
|
||||||
|
topCandidateQuality: 0,
|
||||||
|
bottomCandidate: 0,
|
||||||
|
bottomCandidateDistance: 0,
|
||||||
|
bottomCandidateQuality: 0,
|
||||||
|
topRowsDifferenceMatrix: [0, 0, 0],
|
||||||
|
bottomRowsDifferenceMatrix: [0, 0, 0],
|
||||||
|
|
||||||
|
frontCandidate: 0,
|
||||||
|
backCandidate: 0,
|
||||||
|
},
|
||||||
|
aspectRatioUncertain: false,
|
||||||
|
aspectRatioUncertainEdges: 0,
|
||||||
|
topRowUncertain: false,
|
||||||
|
bottomRowUncertain: false,
|
||||||
|
|
||||||
|
subtitleDetected: false,
|
||||||
|
subtitleScan: {
|
||||||
|
top: -1,
|
||||||
|
bottom: -1,
|
||||||
|
|
||||||
|
regions: {
|
||||||
|
top: {
|
||||||
|
firstBlank: -1,
|
||||||
|
lastBlank: -1,
|
||||||
|
firstSubtitle: -1,
|
||||||
|
lastSubtitle: -1,
|
||||||
|
firstImage: -1,
|
||||||
|
lastImage: -1,
|
||||||
|
uncertain: false,
|
||||||
|
},
|
||||||
|
bottom: {
|
||||||
|
firstBlank: -1,
|
||||||
|
lastBlank: -1,
|
||||||
|
firstSubtitle: -1,
|
||||||
|
lastSubtitle: -1,
|
||||||
|
firstImage: -1,
|
||||||
|
lastImage: -1,
|
||||||
|
uncertain: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
aspectRatioUpdated: false,
|
||||||
|
activeAspectRatio: 0,
|
||||||
|
letterboxSize: 0,
|
||||||
|
letterboxOffset: 0,
|
||||||
|
logoDetected: [false, false, false, false],
|
||||||
|
aspectRatioInvalid: false,
|
||||||
|
notLetterbox: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetGuardLine(results: AardLegacyTestResults) {
|
||||||
|
results.guardLine.front = -1;
|
||||||
|
results.guardLine.back = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetAardTestResults(results: AardLegacyTestResults): void {
|
||||||
|
results.isFinished = false;
|
||||||
|
results.lastStage = 0;
|
||||||
|
results.aspectRatioUpdated = false;
|
||||||
|
results.aspectRatioUncertainReason = null;
|
||||||
|
results.aspectRatioInvalid = false;
|
||||||
|
results.letterboxOrientation = LetterboxOrientation.NotKnown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetSubtitleScanResults(results: AardLegacyTestResults): void {
|
||||||
|
results.subtitleScan.top = -1;
|
||||||
|
results.subtitleScan.bottom = -1;
|
||||||
|
|
||||||
|
results.subtitleScan.regions.top.firstBlank = -1;
|
||||||
|
results.subtitleScan.regions.top.lastBlank = -1;
|
||||||
|
results.subtitleScan.regions.top.firstSubtitle = -1;
|
||||||
|
results.subtitleScan.regions.top.lastSubtitle = -1;
|
||||||
|
results.subtitleScan.regions.top.firstImage = -1;
|
||||||
|
results.subtitleScan.regions.top.lastImage = -1;
|
||||||
|
|
||||||
|
results.subtitleScan.regions.bottom.firstBlank = -1;
|
||||||
|
results.subtitleScan.regions.bottom.lastBlank = -1;
|
||||||
|
results.subtitleScan.regions.bottom.firstSubtitle = -1;
|
||||||
|
results.subtitleScan.regions.bottom.lastSubtitle = -1;
|
||||||
|
results.subtitleScan.regions.bottom.firstImage = -1;
|
||||||
|
results.subtitleScan.regions.bottom.lastImage = -1;
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user