Compare commits

..

No commits in common. "ddffbe92a0f3b664a2cf37b54a1a0637d7519cef" and "61e7351d0b34e1d7119a5dff4b4cf7602f6410c5" have entirely different histories.

21 changed files with 1608 additions and 1019 deletions

View File

@ -52,7 +52,6 @@
"shitiness", "shitiness",
"smallcaps", "smallcaps",
"suboption", "suboption",
"subscan",
"tabitem", "tabitem",
"tablist", "tablist",
"tamius", "tamius",

View File

@ -2,12 +2,10 @@
## v6.0 (current major) ## v6.0 (current major)
### v6.4.0 ### v3.4.0
* In-player UI now appears in full-screen even for websites that use top layer * In-player UI now appears in full-screen even for websites that use top layer
* Embedded sites now inherit settings of the parent frame by default * Embedded sites now inherit settings of the parent frame by default
* 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.
### 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
View File

@ -1,6 +1,6 @@
{ {
"name": "ultrawidify", "name": "ultrawidify",
"version": "6.3.97", "version": "6.3.93",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "ultrawidify", "name": "ultrawidify",
"version": "6.3.97", "version": "6.3.93",
"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": {

View File

@ -1,6 +1,5 @@
import { Action } from '../../../node_modules/vuex/types/index' import { Action } from '../../../node_modules/vuex/types/index'
import { AardPollingOptions } from '../../ext/lib/aard/enums/aard-polling-options.enum' import { AardPollingOptions } from '../../ext/lib/aard/enums/aard-polling-options.enum'
import { AardSubtitleCropMode } from '../../ext/lib/aard/enums/aard-subtitle-crop-mode.enum'
import AntiGradientMode from '../enums/AntiGradientMode.enum' import AntiGradientMode from '../enums/AntiGradientMode.enum'
import AspectRatioType from '../enums/AspectRatioType.enum' import AspectRatioType from '../enums/AspectRatioType.enum'
import CropModePersistence from '../enums/CropModePersistence.enum' import CropModePersistence from '../enums/CropModePersistence.enum'
@ -67,27 +66,6 @@ export interface CommandInterface {
export type SettingsReloadComponent = 'PlayerData' | 'VideoData'; export type SettingsReloadComponent = 'PlayerData' | 'VideoData';
export type SettingsReloadFlags = true | SettingsReloadComponent; export type SettingsReloadFlags = true | SettingsReloadComponent;
export interface AardSubtitleScanOptions {
subtitleCropMode: AardSubtitleCropMode,
resumeAfter: number, // resume after X ms of no subtitle
scanSpacing: number, // repeat subtitle scan every _n_ lines
scanMargin: number, // % of pixels (from edge to start area) that we skip while scanning.
// While technically anything between 0 and 0.5 is valid, the value
// should be somewhere between 0.1-0.3 in order for subtitle scan to
// work properly.
maxValidLetter: number, // if letter is longer than this, something's off.
subtitleSubpixelThresholdOn: number,
subtitleSubpixelThresholdOff: number,
minDetections: number,
minImageLineDetections: number,
maxPotentialSubtitleMisalignment: number, // how many pixels off-center can "potential subtitle" be
refiningScanSpacing: number, // must be base-2
refiningScanInitialIterations: number,
}
export interface AardSettings { export interface AardSettings {
aardType: 'webgl' | 'legacy' | 'auto'; aardType: 'webgl' | 'legacy' | 'auto';
@ -115,7 +93,10 @@ export interface AardSettings {
tickrate: number, // 1 tick every this many milliseconds tickrate: number, // 1 tick every this many milliseconds
}, },
subtitles: AardSubtitleScanOptions, subtitles: {
resetIfDetected?: boolean, // reset if subtitles are detected
resumeAfter?: number, // resume after X ms of no subtitles
},
autoDisable: { // settings for automatically disabling the extension autoDisable: { // settings for automatically disabling the extension
onFirstChange: boolean, onFirstChange: boolean,
@ -152,23 +133,37 @@ export interface AardSettings {
// pls deprecate and move things used // pls deprecate and move things used
edgeDetection: { edgeDetection: {
gradientThreshold: number, // if more than this percentage (0-1) is detected as gradient, we mark edge as gradient slopeTestWidth: number,
gradientTestMinDelta: number, // if difference between test row and before row is MORE than this -> not gradient gradientTestSamples: number, // we check this many pixels below (or above) the suspected edge to check for gradient
gradientTestMinDeltaAfter: number, // if difference between test row and after row is LESS than this -> not gradient gradientTestBlackThreshold: number, // if pixel in test sample is brighter than that, we aren't looking at gradient
gradientTestMaxDeltaAfter: number, // if difference between test row and after row is MORE than this -> not gradient gradientTestDeltaThreshold: number, // if delta between two adjacent pixels in gradient test exceeds this, it's not gradient
maxLetterboxOffset: number, // Upper and lower letterbox can be different by this many (% of height) gradientTestMinDelta: number, // if last pixels of the test sample is less than this brighter than the first -> not gradient
minValidImage: number, // if more than this % (0-1) of row is image, we confirm image regardless of other criteria except gradient thresholds: {
maxEdgeSegments: number, // if edge has more than this many segments, we consider it unreliable edgeDetectionLimit: number, // during scanning of the edge, quit after edge gets detected at this many points
minEdgeSegmentSize: number, minQualitySingleEdge: number, // At least one of the detected must reach this quality
averageEdgeThreshold: number, // average edge must be this many px minQualitySecondEdge: number, // The other edge must reach this quality (must be smaller or equal to single edge quality)
},
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
} }
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
},
pillarTest: { pillarTest: {
ignoreThinPillarsPx: number, // ignore pillars that are less than this many pixels thick. ignoreThinPillarsPx: number, // ignore pillars that are less than this many pixels thick.
allowMisaligned: number // left and right edge can vary this much (%) allowMisaligned: number // left and right edge can vary this much (%)

View File

@ -146,45 +146,6 @@
</div> </div>
</div> </div>
<div class="settings-segment">
<div class="warning-area">
Subtitle detection is experimental to high heaven.
</div>
<div class="field">
<div class="label">Subtitle detection:</div>
<div class="select">
<select v-model="settings.active.arDetect.subtitles.subtitleCropMode" @change="settings.saveWithoutReload">
<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.ResetAndDisable">Stop autodetection if subtitles are detected</option>
<option :value="AardSubtitleCropMode.CropSubtitles">Always crop subtitles</option>
</select>
</div>
</div>
<div v-if="settings.active.arDetect.subtitles.subtitleCropMode === AardSubtitleCropMode.ResetAR" class="field">
<div class="label">Wait before resuming detection:</div>
<div class="range-input">
<input
type="range"
:value="Math.log(settings.active.arDetect.subtitles.resumeAfter / 10)"
@change="setSubtitleTimeout($event.target.value, 10)"
min="2.3"
max="9.3"
step="0.01"
/>
<input
:value="settings.active.arDetect.subtitles.resumeAfter / 1000"
@change="setSubtitleTimeout($event.target.value, 1000)"
class="input"
type="text"
>
<div class="unit">s</div>
</div>
</div>
</div>
<div v-if="settings.active.ui.devMode" class="settings-segment"> <div v-if="settings.active.ui.devMode" class="settings-segment">
<p> <p>
<b>Debug options</b> <b>Debug options</b>
@ -230,7 +191,6 @@ import CropModePersistence from '../../../common/enums/CropModePersistence.enum'
import AlignmentOptionsControlComponent from './AlignmentOptionsControlComponent.vue'; import AlignmentOptionsControlComponent from './AlignmentOptionsControlComponent.vue';
import JsonEditor from '@csui/src/components/JsonEditor'; import JsonEditor from '@csui/src/components/JsonEditor';
import {AardPollingOptions} from '@src/ext/lib/aard/enums/aard-polling-options.enum'; import {AardPollingOptions} from '@src/ext/lib/aard/enums/aard-polling-options.enum';
import {AardSubtitleCropMode} from '@src/ext/lib/aard/enums/aard-subtitle-crop-mode.enum';
export default { export default {
components: { components: {
@ -257,7 +217,6 @@ export default {
// enums n stuff // enums n stuff
AardPollingOptions, AardPollingOptions,
AardSubtitleCropMode
} }
}, },
computed: { computed: {
@ -292,10 +251,6 @@ export default {
this.settings.active.arDetect.autoDisable.ifNotChangedTimeout = Math.floor(Math.pow(Math.E, event)) * multiplier; this.settings.active.arDetect.autoDisable.ifNotChangedTimeout = Math.floor(Math.pow(Math.E, event)) * multiplier;
this.settings.saveWithoutReload(); this.settings.saveWithoutReload();
}, },
setSubtitleTimeout(event, multiplier) {
this.settings.active.arDetect.subtitles.resumeAfter = Math.floor(Math.pow(Math.E, event)) * multiplier;
this.settings.saveWithoutReload();
},
refreshGraph() { refreshGraph() {
this.eventBus.sendToTunnel('get-aard-timing'); this.eventBus.sendToTunnel('get-aard-timing');
}, },

View File

@ -8,9 +8,8 @@
<h2>6.4.0</h2> <h2>6.4.0</h2>
<ul> <ul>
<li>In-player UI can now appear in full-screen for websites that put players into top layer</li> <li>In-player UI can now appear in full-screen for websites that put players into top layer</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>Embedded sites now inherit settings of the parent frame</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>
</ul> </ul>
</div> </div>

View File

@ -259,14 +259,6 @@ const ExtensionConfPatch = Object.freeze([
userOptions.arDetect.subtitles = defaultOptions.arDetect.subtitles; userOptions.arDetect.subtitles = defaultOptions.arDetect.subtitles;
userOptions.arDetect.autoDisable = defaultOptions.arDetect.autoDisable; userOptions.arDetect.autoDisable = defaultOptions.arDetect.autoDisable;
} }
},
{
forVersion: '6.3.97',
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => {
userOptions.arDetect.letterboxOrientationScan = defaultOptions.arDetect.letterboxOrientationScan;
userOptions.arDetect.edgeDetection = defaultOptions.arDetect.edgeDetection;
userOptions.arDetect.subtitles = defaultOptions.arDetect.subtitles;
}
} }
]); ]);

View File

@ -11,7 +11,6 @@ import BrowserDetect from './BrowserDetect';
import { Extension } from 'typescript'; import { Extension } from 'typescript';
import EmbeddedContentSettingsOverridePolicy from '../../common/enums/EmbeddedContentSettingsOverridePolicy.enum'; import EmbeddedContentSettingsOverridePolicy from '../../common/enums/EmbeddedContentSettingsOverridePolicy.enum';
import { AardPollingOptions } from '../lib/aard/enums/aard-polling-options.enum'; import { AardPollingOptions } from '../lib/aard/enums/aard-polling-options.enum';
import { AardSubtitleCropMode } from '../lib/aard/enums/aard-subtitle-crop-mode.enum';
if(Debug.debug) if(Debug.debug)
console.log("Loading: ExtensionConf.js"); console.log("Loading: ExtensionConf.js");
@ -29,26 +28,9 @@ const ExtensionConf: SettingsInterface = {
runOnSmallVideos: AardPollingOptions.Reduced runOnSmallVideos: AardPollingOptions.Reduced
}, },
letterboxOrientationScan: {
letterboxLimit: 8,
pillarboxLimit: 8
},
subtitles: { subtitles: {
subtitleCropMode: AardSubtitleCropMode.ResetAR, resetIfDetected: true,
resumeAfter: 5000, resumeAfter: 5000,
scanSpacing: 5,
scanMargin: 0.25,
maxValidLetter: 24,
subtitleSubpixelThresholdOff: 8,
subtitleSubpixelThresholdOn: 192,
minDetections: 8,
minImageLineDetections: 8,
refiningScanSpacing: 8,
refiningScanInitialIterations: 12,
maxPotentialSubtitleMisalignment: 32,
}, },
earlyStopOptions: { earlyStopOptions: {
@ -100,17 +82,36 @@ const ExtensionConf: SettingsInterface = {
staticRows: 9, // forms grid with staticSampleCols. Determined in the same way. For black frame checks staticRows: 9, // forms grid with staticSampleCols. Determined in the same way. For black frame checks
}, },
edgeDetection: { edgeDetection: {
maxLetterboxOffset: 0.05, slopeTestWidth: 8,
gradientTestSamples: 8,
gradientTestBlackThreshold: 16,
gradientTestDeltaThreshold: 32,
gradientTestMinDelta: 8,
gradientThreshold: 0.5, // if more than this percentage (0-1) is detected as gradient, we mark edge as gradient thresholds: {
gradientTestMinDelta: 8, // if difference between test row and before row is MORE than this -> not gradient edgeDetectionLimit: 12,
gradientTestMinDeltaAfter: 2, // if difference between test row and after row is LESS than this -> not gradient minQualitySingleEdge: 6,
gradientTestMaxDeltaAfter: 12, // if difference between test row and after row is MORE than this -> not gradient minQualitySecondEdge: 3,
},
minValidImage: 0.7, // if more than this % (0-1) of row is image, we confirm image regardless of other criteria except gradient maxLetterboxOffset: 0.1,
maxEdgeSegments: 8, // if edge has more than this many segments, we consider it unreliable
minEdgeSegmentSize: 2, sampleWidth: 8, // we take a sample this wide for edge detection
averageEdgeThreshold: 16, // average(ish) edge must be this many px 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
}, },
pillarTest: { pillarTest: {
ignoreThinPillarsPx: 5, // ignore pillars that are less than this many pixels thick. ignoreThinPillarsPx: 5, // ignore pillars that are less than this many pixels thick.

File diff suppressed because it is too large Load Diff

View File

@ -196,7 +196,14 @@ export class AardDebugUi {
<h2 style="color: #fa6; margin-bottom: 1rem">Detailed performance analysis:</h2> <h2 style="color: #fa6; margin-bottom: 1rem">Detailed performance analysis:</h2>
<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)} <span style="color: #fff">Raw times (cumulative; -1 = test was skipped):</span><br/>
draw <span style="color: #fa6; margin-right: 0.5rem;">${this.aard.timer.current.draw.toFixed(2)}ms</span>
get data <span style="color: #fa6; margin-right: 0.5rem;">${this.aard.timer.current.getImage.toFixed(2)}ms</span>
black lv. <span style="color: #fa6; margin-right: 0.5rem;">${this.aard.timer.current.fastBlackLevel.toFixed(2)}ms</span>
guard/image <span style="color: #fa6; margin-right: 0.5rem;">${this.aard.timer.current.guardLine.toFixed(3)}ms</span>
edge <span style="color: #fa6; margin-right: 0.5rem;">${this.aard.timer.current.edgeScan.toFixed(2)}ms</span>
gradient <span style="color: #fa6; margin-right: 0.5rem;">${this.aard.timer.current.gradient.toFixed(3)}ms</span>
post <span style="color: #fa6; margin-right: 0.5rem;">${this.aard.timer.current.scanResults.toFixed(2)}ms</span>
</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">
@ -205,7 +212,14 @@ export class AardDebugUi {
</div> </div>
<div style="margin-bottom: 1rem;"> <div style="margin-bottom: 1rem;">
${this.generateRawTimes(this.aard.timer.average)} <span style="color: #fff">Raw times (cumulative; -1 = test was skipped):</span><br/>
draw <span style="color: #fa6; margin-right: 0.5rem;">${this.aard.timer.average.draw.toFixed(2)}ms</span>
get data <span style="color: #fa6; margin-right: 0.5rem;">${this.aard.timer.average.getImage.toFixed(2)}ms</span>
black lv. <span style="color: #fa6; margin-right: 0.5rem;">${this.aard.timer.average.fastBlackLevel.toFixed(2)}ms</span>
guard/image <span style="color: #fa6; margin-right: 0.5rem;">${this.aard.timer.average.guardLine.toFixed(3)}ms</span>
edge <span style="color: #fa6; margin-right: 0.5rem;">${this.aard.timer.average.edgeScan.toFixed(2)}ms</span>
gradient <span style="color: #fa6; margin-right: 0.5rem;">${this.aard.timer.average.gradient.toFixed(3)}ms</span>
post <span style="color: #fa6; margin-right: 0.5rem;">${this.aard.timer.average.scanResults.toFixed(2)}ms</span>
</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">
@ -213,9 +227,6 @@ export class AardDebugUi {
<div style="flex-grow: 1;">${this.generateMiniGraphBar(this.aard.timer.average, true)}</div> <div style="flex-grow: 1;">${this.generateMiniGraphBar(this.aard.timer.average, true)}</div>
</div> </div>
<div style="margin-bottom: 1rem;">
${this.generateRawTimes(this.aard.timer.average)}
</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>
@ -263,20 +274,6 @@ export class AardDebugUi {
`; `;
} }
private generateRawTimes(timer) {
return `
<span style="color: #fff">Raw times (cumulative; -1 = test was skipped):</span><br/>
draw <span style="color: #fa6; margin-right: 0.5rem;">${timer.draw.toFixed(2)}ms</span>
get data <span style="color: #fa6; margin-right: 0.5rem;">${timer.getImage.toFixed(2)}ms</span>
black lv. <span style="color: #fa6; margin-right: 0.5rem;">${timer.fastBlackLevel.toFixed(2)}ms</span>
guard/image <span style="color: #fa6; margin-right: 0.5rem;">${timer.guardLine.toFixed(3)}ms</span>
edge <span style="color: #fa6; margin-right: 0.5rem;">${timer.edgeScan.toFixed(2)}ms</span>
gradient <span style="color: #fa6; margin-right: 0.5rem;">${timer.gradient.toFixed(3)}ms</span>
post <span style="color: #fa6; margin-right: 0.5rem;">${timer.scanResults.toFixed(2)}ms</span>
subtitle <span style="color: #fa6; margin-right: 0.5rem;">${timer.subtitleScan.toFixed(2)}ms</span>
`;
}
private generateMiniGraphBar(perf: AardPerformanceData, detailed: boolean = false) { private generateMiniGraphBar(perf: AardPerformanceData, detailed: boolean = false) {
if (!perf) { if (!perf) {
return ` return `
@ -313,12 +310,8 @@ export class AardDebugUi {
const scanResults = Math.max(perf.scanResults - total, 0); const scanResults = Math.max(perf.scanResults - total, 0);
total += scanResults; total += scanResults;
const subtitleScanStart = scanResultsStart + scanResults;
const subtitleScan = Math.max(perf.scanResults - total, 0);
total += subtitleScan;
return ` return `
<div style="width: calc(100% - 2rem); position: relative; text-align: right;"> <div style="width: 100%; position: relative; text-align: right;">
${detailed ? '' : `${total.toFixed()} ms`} ${detailed ? '' : `${total.toFixed()} ms`}
<div style="position: absolute; top: 0; left: 0; width: ${total}%; background: #fff; height: 2px;"></div> <div style="position: absolute; top: 0; left: 0; width: ${total}%; background: #fff; height: 2px;"></div>
${detailed ? `<div style="position: absolute; top: 0; left: ${total}%; height: 100px; width: 1px; border-left: 1px dotted rgba(255,255,255,0.5)"></div> ` : ''} ${detailed ? `<div style="position: absolute; top: 0; left: ${total}%; height: 100px; width: 1px; border-left: 1px dotted rgba(255,255,255,0.5)"></div> ` : ''}
@ -346,11 +339,6 @@ export class AardDebugUi {
${this.getBarLabel(0, scanResults + scanResultsStart, 88, `total: ${total.toFixed(2)} ms`, detailed, 'color: #fff;')} ${this.getBarLabel(0, scanResults + scanResultsStart, 88, `total: ${total.toFixed(2)} ms`, detailed, 'color: #fff;')}
<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(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>
<div style="position: absolute; top: ${detailed ? '-12px' : '0'}; left: 33.333%; width: 1px; border-left: 1px dashed #ff0; height: ${detailed ? '112px' : '12px'}; padding-left: 2px; background-color: #000; z-index: ${detailed ? '5' : '2'}000;">30fps</div> <div style="position: absolute; top: ${detailed ? '-12px' : '0'}; left: 33.333%; width: 1px; border-left: 1px dashed #ff0; height: ${detailed ? '112px' : '12px'}; padding-left: 2px; background-color: #000; z-index: ${detailed ? '5' : '2'}000;">30fps</div>
@ -358,7 +346,6 @@ export class AardDebugUi {
`; `;
} }
_lastAr: undefined;
updateTestResults(testResults) { updateTestResults(testResults) {
this.updatePerformanceResults(); this.updatePerformanceResults();
@ -369,22 +356,17 @@ export class AardDebugUi {
const resultsDiv = document.getElementById('uw-aard-results'); const resultsDiv = document.getElementById('uw-aard-results');
const ar = testResults.activeAspectRatio.toFixed(3);
let out = ` let out = `
LAST STAGE: ${testResults.lastStage} | black level: ${testResults.blackLevel}, threshold: ${testResults.blackThreshold} LAST STAGE: ${testResults.lastStage} | black level: ${testResults.blackLevel}, threshold: ${testResults.blackThreshold}
-- ASPECT RATIO -- ASPECT RATIO
Active: ${ar}, changed since last check? ${testResults.aspectRatioUpdated} letterbox width: ${testResults.letterboxWidth} offset ${testResults.letterboxOffset}<br/> Active: ${testResults.activeAspectRatio.toFixed(3)}, changed since last check? ${testResults.aspectRatioUpdated} letterbox width: ${testResults.letterboxWidth} offset ${testResults.letterboxOffset}
<sup>(last: ${this._lastAr})</sup>
image in black level probe (aka "not letterbox"): ${testResults.notLetterbox} image in black level probe (aka "not letterbox"): ${testResults.notLetterbox}
`;
this._lastAr = ar;
`;
if (testResults.notLetterbox) { if (testResults.notLetterbox) {
resultsDiv.innerHTML = out; resultsDiv.textContent = out;
return; return;
} }
out = `${out} out = `${out}
@ -424,7 +406,7 @@ export class AardDebugUi {
`} `}
`; `;
resultsDiv.innerHTML = out; resultsDiv.textContent = out;
} }
private setOverlayVisibility() { private setOverlayVisibility() {

View File

@ -7,7 +7,6 @@ export interface AardPerformanceData {
guardLine: number; // actually times both guard line and image line checks guardLine: number; // actually times both guard line and image line checks
edgeScan: number; // includes validation step edgeScan: number; // includes validation step
gradient: number; gradient: number;
subtitleScan: number;
scanResults: number; scanResults: number;
} }
@ -42,8 +41,7 @@ export class AardTimer {
guardLine: -1, guardLine: -1,
edgeScan: -1, edgeScan: -1,
gradient: -1, gradient: -1,
scanResults: -1, scanResults: -1
subtitleScan: -1,
} }
}; };
@ -55,7 +53,6 @@ export class AardTimer {
this.aardPerformanceDataBuffer[index].edgeScan = -1; this.aardPerformanceDataBuffer[index].edgeScan = -1;
this.aardPerformanceDataBuffer[index].gradient = -1; this.aardPerformanceDataBuffer[index].gradient = -1;
this.aardPerformanceDataBuffer[index].scanResults = -1; this.aardPerformanceDataBuffer[index].scanResults = -1;
this.aardPerformanceDataBuffer[index].subtitleScan = -1;
} }
next() { next() {
@ -76,7 +73,6 @@ export class AardTimer {
this.lastChange.edgeScan = this.current.edgeScan; this.lastChange.edgeScan = this.current.edgeScan;
this.lastChange.gradient = this.current.gradient; this.lastChange.gradient = this.current.gradient;
this.lastChange.scanResults = this.current.scanResults; this.lastChange.scanResults = this.current.scanResults;
this.lastChange.subtitleScan = this.current.scanResults;
} }
getAverage() { getAverage() {
@ -112,6 +108,5 @@ export class AardTimer {
this.average.edgeScan /= this.aardPerformanceDataBuffer.length; this.average.edgeScan /= this.aardPerformanceDataBuffer.length;
this.average.gradient /= this.aardPerformanceDataBuffer.length; this.average.gradient /= this.aardPerformanceDataBuffer.length;
this.average.scanResults /= this.aardPerformanceDataBuffer.length; this.average.scanResults /= this.aardPerformanceDataBuffer.length;
this.average.subtitleScan /= this.aardPerformanceDataBuffer.length;
} }
} }

View File

@ -1,5 +0,0 @@
export enum AardUncertainReason {
InsufficientEdgeDetectionQuality = 1,
LetterboxNotCenteredEnough = 2,
TopAndBottomRowMismatch = 3,
}

View File

@ -1,6 +0,0 @@
export enum AardSubtitleCropMode {
DisableScan = 0,
ResetAR = 1,
ResetAndDisable = 2,
CropSubtitles = 3
}

View File

@ -1,6 +0,0 @@
export enum Edge {
Top = 1,
Left = 2,
Bottom = 3,
Right = 4
}

View File

@ -1,7 +0,0 @@
export enum LetterboxOrientation {
NotKnown = -1,
NotLetterbox = 0,
Letterbox = 1,
Pillarbox = 2,
Both = 3
}

View File

@ -25,7 +25,6 @@ precision mediump float;
uniform sampler2D u_texture; uniform sampler2D u_texture;
// uniform sampler1D u_colorTexture; // Array of replacement colors // uniform sampler1D u_colorTexture; // Array of replacement colors
uniform vec3 u_colors[16]; uniform vec3 u_colors[16];
uniform vec3 u_colors2[16];
varying vec2 vTextureCoord; varying vec2 vTextureCoord;
void main() { void main() {
@ -58,26 +57,6 @@ void main() {
else if (alphaIndex == 14) selectedColor = u_colors[14]; else if (alphaIndex == 14) selectedColor = u_colors[14];
else selectedColor = u_colors[15]; else selectedColor = u_colors[15];
gl_FragColor = vec4(selectedColor, 1.0);
} else if (alphaIndex < 32) {
vec3 selectedColor;
if (alphaIndex == 0) selectedColor = u_colors[0];
else if (alphaIndex == 1) selectedColor = u_colors2[1];
else if (alphaIndex == 2) selectedColor = u_colors2[2];
else if (alphaIndex == 3) selectedColor = u_colors2[3];
else if (alphaIndex == 4) selectedColor = u_colors2[4];
else if (alphaIndex == 5) selectedColor = u_colors2[5];
else if (alphaIndex == 6) selectedColor = u_colors2[6];
else if (alphaIndex == 7) selectedColor = u_colors2[7];
else if (alphaIndex == 8) selectedColor = u_colors2[8];
else if (alphaIndex == 9) selectedColor = u_colors2[9];
else if (alphaIndex == 10) selectedColor = u_colors2[10];
else if (alphaIndex == 11) selectedColor = u_colors2[11];
else if (alphaIndex == 12) selectedColor = u_colors2[12];
else if (alphaIndex == 13) selectedColor = u_colors2[13];
else if (alphaIndex == 14) selectedColor = u_colors2[14];
else selectedColor = u_colors2[15];
gl_FragColor = vec4(selectedColor, 1.0); gl_FragColor = vec4(selectedColor, 1.0);
} else { // red channel only as fallback } else { // red channel only as fallback
gl_FragColor = vec4(color.r, 0.0, 0.0, 1.0); gl_FragColor = vec4(color.r, 0.0, 0.0, 1.0);
@ -98,11 +77,7 @@ export enum GlDebugType {
EdgeScanHit = 9, EdgeScanHit = 9,
SlopeTestDarkOk = 10, SlopeTestDarkOk = 10,
SlopeTestDarkViolation = 11, SlopeTestDarkViolation = 11,
SubtitleThresholdOff = 12,
SubtitleThresholdOn = 13,
SubtitleThresholdNone = 14,
LetterboxOrientationScanImageDetection = 15,
LetterboxOrientationScanTrace = 16,
} }
export class GlDebugCanvas extends GlCanvas { export class GlDebugCanvas extends GlCanvas {
@ -120,29 +95,11 @@ export class GlDebugCanvas extends GlCanvas {
0.4, 0.4, 1.0, // 9 - edge scan hit 0.4, 0.4, 1.0, // 9 - edge scan hit
0.2, 0.4, 0.6, // 10 - slope test ok 0.2, 0.4, 0.6, // 10 - slope test ok
1.0, 0.0, 0.0, // 11 - slope test fail 1.0, 0.0, 0.0, // 11 - slope test fail
0.1, 0.1, 0.3, // 12 - first subtitle off pixel 0.0, 0.0, 0.0, // 12
1.0, 1.6, 0.0, // 13 - first subtitle on pixel 0.0, 0.0, 0.0,
7.0, 0.2, 0.0, // 14 - pixel subtitle — no threshold 0.0, 0.0, 0.0,
1.0, 0.0, 0.0, // 15 - letterbox scan image detection 0.0, 0.0, 0.0,
]; ];
private debugColors2 = [
0.4, 0.3, 0.2, // 16 - letterbox scan image trace
0.0, 0.0, 0.0, // 17
0.0, 0.0, 0.0, // 18
0.0, 0.0, 0.0, // 19
0.0, 0.0, 0.0, // 20
0.0, 0.0, 0.0, // 21
0.0, 0.0, 0.0, // 22
0.0, 0.0, 0.0, // 23
0.0, 0.0, 0.0, // 24
0.0, 0.0, 0.0, // 25
0.0, 0.0, 0.0, // 26
0.0, 0.0, 0.0, // 27
0.0, 0.0, 0.0, // 28
0.0, 0.0, 0.0, // 29
0.0, 0.0, 0.0, // 30
0.0, 0.0, 0.0, // 31
]
constructor (options: GlCanvasOptions) { constructor (options: GlCanvasOptions) {
super(options); super(options);
@ -175,7 +132,6 @@ export class GlDebugCanvas extends GlCanvas {
enableFx() { enableFx() {
this.gl.useProgram(this.programInfo.program) this.gl.useProgram(this.programInfo.program)
this.gl.uniform3fv((this.programInfo.uniformLocations as any).debugColors, this.debugColors); this.gl.uniform3fv((this.programInfo.uniformLocations as any).debugColors, this.debugColors);
this.gl.uniform3fv((this.programInfo.uniformLocations as any).debugColors2, this.debugColors2);
} }
drawBuffer(buffer: Uint8Array) { drawBuffer(buffer: Uint8Array) {
@ -186,7 +142,6 @@ export class GlDebugCanvas extends GlCanvas {
super.initWebgl(); super.initWebgl();
(this.programInfo.uniformLocations as any).debugColors = this.gl.getUniformLocation(this.programInfo.program, 'u_colors'); (this.programInfo.uniformLocations as any).debugColors = this.gl.getUniformLocation(this.programInfo.program, 'u_colors');
(this.programInfo.uniformLocations as any).debugColors2 = this.gl.getUniformLocation(this.programInfo.program, 'u_colors2');
} }
protected updateTextureBuffer(buffer: Uint8Array) { protected updateTextureBuffer(buffer: Uint8Array) {

View File

@ -19,9 +19,6 @@ export interface AardDetectionSample {
bottom?: Int16Array; bottom?: Int16Array;
left?: Int16Array; left?: Int16Array;
right?: Int16Array; right?: Int16Array;
start?: Int16Array;
end?: Int16Array;
} }
export function generateSampleArray(samples: number, width: number, topBottom: boolean = true) { export function generateSampleArray(samples: number, width: number, topBottom: boolean = true) {

View File

@ -1,133 +1,127 @@
import { AardSettings } from '../../../../common/interfaces/SettingsInterface' import { 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 AardTestResults { export interface AardTestResults {
isFinished: boolean, isFinished: boolean,
lastStage: number, lastStage: number,
letterboxOrientation: LetterboxOrientation, notLetterbox: boolean,
lastValidLetterboxOrientation: LetterboxOrientation,
subtitleDetected: boolean,
blackLevel: number, // is cumulative blackLevel: number, // is cumulative
blackThreshold: number, // is cumulative blackThreshold: number, // is cumulative
guardLine: { guardLine: {
front: number, top: number, // is cumulative
back: number, bottom: number, // is cumulative
invalidated: boolean,
cornerViolated: [boolean, boolean, boolean, boolean],
cornerPixelsViolated: [0,0,0,0]
},
imageLine: {
top: number, // is cumulative
bottom: number, // is cumulative
invalidated: boolean
}, },
aspectRatioCheck: { aspectRatioCheck: {
frontCandidate: number, topRows: [number, number, number],
backCandidate: 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],
}, },
aspectRatioUncertain: boolean, aspectRatioUncertain: boolean,
topRowUncertain: boolean,
bottomRowUncertain: boolean,
aspectRatioUpdated: boolean, aspectRatioUpdated: boolean,
activeAspectRatio: number, // is cumulative activeAspectRatio: number, // is cumulative
letterboxSize: number, letterboxWidth: number,
letterboxOffset: number, letterboxOffset: number,
aspectRatioInvalid: boolean, logoDetected: [boolean, boolean, boolean, boolean]
subtitleScan: { aspectRatioInvalid: boolean
top: number, aspectRatioUncertainReason?: string
bottom: number, aspectRatioInvalidReason?: string
regions: {
top: AardTestResult_SubtitleRegion,
bottom: AardTestResult_SubtitleRegion
}
},
aspectRatioUncertainReason?: AardUncertainReason,
aspectRatioInvalidReason?: string,
} }
export function initAardTestResults(settings: AardSettings): AardTestResults { export function initAardTestResults(settings: AardSettings): AardTestResults {
return { return {
isFinished: true, isFinished: true,
lastStage: 0, lastStage: 0,
letterboxOrientation: LetterboxOrientation.NotKnown, notLetterbox: false,
lastValidLetterboxOrientation: LetterboxOrientation.NotKnown,
blackLevel: settings.blackLevels.defaultBlack, blackLevel: settings.blackLevels.defaultBlack,
blackThreshold: 16, blackThreshold: 16,
guardLine: { guardLine: {
front: -1,
back: -1,
},
aspectRatioCheck: {
frontCandidate: 0,
backCandidate: 0,
},
aspectRatioUncertain: false,
subtitleDetected: false,
subtitleScan: {
top: -1, top: -1,
bottom: -1, bottom: -1,
invalidated: false,
regions: { cornerViolated: [false, false, false, false],
top: { cornerPixelsViolated: [0,0,0,0]
firstBlank: -1,
lastBlank: -1,
firstSubtitle: -1,
lastSubtitle: -1,
firstImage: -1,
lastImage: -1,
uncertain: false,
}, },
bottom: { imageLine: {
firstBlank: -1, top: -1,
lastBlank: -1, bottom: -1,
firstSubtitle: -1, invalidated: false,
lastSubtitle: -1,
firstImage: -1,
lastImage: -1,
uncertain: 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],
},
aspectRatioUncertain: false,
topRowUncertain: false,
bottomRowUncertain: false,
aspectRatioUpdated: false, aspectRatioUpdated: false,
activeAspectRatio: 0, activeAspectRatio: 0,
letterboxSize: 0, letterboxWidth: 0,
letterboxOffset: 0, letterboxOffset: 0,
logoDetected: [false, false, false, false],
aspectRatioInvalid: false, aspectRatioInvalid: false,
} }
} }
export function resetGuardLine(results: AardTestResults) { export function resetGuardLine(results: AardTestResults) {
results.guardLine.front = -1; results.guardLine.top = -1;
results.guardLine.back = -1; results.guardLine.bottom = -1;
results.imageLine.invalidated = false;
results.guardLine.invalidated = false;
results.guardLine.cornerViolated[0] = false;
results.guardLine.cornerViolated[1] = false;
results.guardLine.cornerViolated[2] = false;
results.guardLine.cornerViolated[3] = false;
results.guardLine.cornerPixelsViolated[0] = 0;
results.guardLine.cornerPixelsViolated[1] = 0;
results.guardLine.cornerPixelsViolated[2] = 0;
results.guardLine.cornerPixelsViolated[3] = 0;
} }
export function resetAardTestResults(results: AardTestResults): void { export function resetAardTestResults(results: AardTestResults): void {
results.isFinished = false; results.isFinished = false;
results.lastStage = 0; results.lastStage = 0;
results.notLetterbox = false;
results.imageLine.invalidated = false;
results.guardLine.invalidated = false;
results.guardLine.cornerViolated[0] = false;
results.guardLine.cornerViolated[1] = false;
results.guardLine.cornerViolated[2] = false;
results.guardLine.cornerViolated[3] = false;
results.guardLine.cornerPixelsViolated[0] = 0;
results.guardLine.cornerPixelsViolated[1] = 0;
results.guardLine.cornerPixelsViolated[2] = 0;
results.guardLine.cornerPixelsViolated[3] = 0;
// results.letterboxWidth = 0;
// results.letterboxOffset = 0;
results.aspectRatioUpdated = false; results.aspectRatioUpdated = false;
results.aspectRatioUncertainReason = null; results.aspectRatioUncertainReason = null;
results.topRowUncertain = false;
results.bottomRowUncertain = false;
results.aspectRatioInvalid = false; results.aspectRatioInvalid = false;
results.letterboxOrientation = LetterboxOrientation.NotKnown;
} }
export function resetSubtitleScanResults(results: AardTestResults): 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;
}

View File

@ -2,7 +2,6 @@ export interface AardTimers {
autoDisableAt: number | undefined; autoDisableAt: number | undefined;
nextFrameCheckTime: number; nextFrameCheckTime: number;
reducedPollingNextCheckTime: number; reducedPollingNextCheckTime: number;
pauseUntil: number;
} }
export function initAardTimers(): AardTimers { export function initAardTimers(): AardTimers {
@ -10,6 +9,5 @@ export function initAardTimers(): AardTimers {
nextFrameCheckTime: 0, nextFrameCheckTime: 0,
reducedPollingNextCheckTime: 0, reducedPollingNextCheckTime: 0,
autoDisableAt: undefined, autoDisableAt: undefined,
pauseUntil: 0,
}; };
} }

View File

@ -2,7 +2,7 @@
"manifest_version": 3, "manifest_version": 3,
"name": "Ultrawidify", "name": "Ultrawidify",
"description": "Removes black bars on ultrawide videos and offers advanced options to fix aspect ratio.", "description": "Removes black bars on ultrawide videos and offers advanced options to fix aspect ratio.",
"version": "6.3.97", "version": "6.3.93",
"icons": { "icons": {
"32":"res/icons/uw-32.png", "32":"res/icons/uw-32.png",
"64":"res/icons/uw-64.png" "64":"res/icons/uw-64.png"