Compare commits

..

No commits in common. "54c44b4379a9cbf187357ec75f136fc6f51002c3" and "7c5e4101b0279d2ff1c26833c6e3e793c03f7c34" have entirely different histories.

8 changed files with 81 additions and 283 deletions

View File

@ -166,9 +166,9 @@ export interface AardSettings {
gradientTestMinDelta: number, // if last pixels of the test sample is less than this brighter than the first -> 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)
edgeDetectionLimit: 8, // during scanning of the edge, quit after edge gets detected at this many points
minQualitySingleEdge: 6, // At least one of the detected must reach this quality
minQualitySecondEdge: 3, // The other edge must reach this quality (must be smaller or equal to single edge quality)
}
maxLetterboxOffset: 0.1, // Upper and lower letterbox can be different by this many (% of height)

View File

@ -6,61 +6,10 @@
:style="uwTriggerRegionConf"
@mouseenter="showUwWindow"
>
<!-- <h1>Aspect ratio controls</h1>
<div>Hover to activate</div> -->
[ |> ]
<h1>Aspect ratio controls</h1>
<div>Hover to activate</div>
</div>
<div class="context-spawn uw-clickable">
<GhettoContextMenu alignment="right">
<template v-slot:activator>
<div class="context-item">
Ultrawidify
</div>
</template>
<slot>
<div class="menu-width">
<GhettoContextMenu alignment="right">
<template v-slot:activator>
<div class="context-item">
Crop
</div>
</template>
<slot>
<div>MEnu item 1</div>
<div>Menu item 2</div>
<div>Menu item 3</div>
</slot>
</GhettoContextMenu>
<GhettoContextMenu alignment="right">
<template v-slot:activator>
<div class="context-item">
Stretch
</div>
</template>
<slot>
<div>Menu item 4</div>
<div>Menu item 5</div>
<div>Menu item 6</div>
</slot>
</GhettoContextMenu>
<GhettoContextMenu alignment="right">
<template v-slot:activator>
<div class="context-item">
Align
</div>
</template>
</GhettoContextMenu>
<button @click="showUiWindow()">Extension settings</button>
<button @click="showUiWindow()">Not working?</button>
</div>
</slot>
</GhettoContextMenu>
</div>
<div
v-if="settingsInitialized && uwWindowVisible"
class="uw-window flex flex-col uw-clickable"
@ -81,8 +30,7 @@
</template>
<script>
import PlayerUIWindow from './src/PlayerUIWindow.vue';
import GhettoContextMenu from './src/components/GhettoContextMenu.vue';
import PlayerUIWindow from './src/PlayerUIWindow.vue'
import BrowserDetect from '../ext/conf/BrowserDetect';
import Logger from '../ext/lib/Logger';
import Settings from '../ext/lib/Settings';
@ -91,8 +39,7 @@ import UIProbeMixin from './src/utils/UIProbeMixin';
export default {
components: {
PlayerUIWindow,
GhettoContextMenu
PlayerUIWindow
},
mixins: [
UIProbeMixin
@ -355,6 +302,7 @@ export default {
.uw-hover {
position: absolute;
z-index: 999999999999999999;
}
@ -394,31 +342,4 @@ export default {
}
}
.context-spawn {
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
display: flex;
flex-direction: row;
align-content: center;
align-items: center;
// width: 100%;
// height: 100%;
padding: 2rem;
color: #fff;
.context-item {
font-size: .95rem;
padding: 1rem 1.6rem;
background-color: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(16px) saturate(120%);
white-space: nowrap;
}
}
</style>

View File

@ -1,71 +0,0 @@
<template>
<div class="context-container" @mouseleave="hideContextMenu()">
<div class="activator"
@click="showContextMenu()"
@mouseenter="showContextMenu()"
>
<slot name="activator"></slot>
</div>
<div
v-if="contextMenuVisible"
class="context-menu"
:class="{
'menu-left': alignment === 'left',
'menu-right': alignment !== 'left'
}"
@mouseleave="hideContextMenu()"
>
<slot></slot>
</div>
</div>
</template>
<script>
export default {
props: {
alignment: String,
},
data() {
return {
contextMenuVisible: false,
contextMenuHideTimeout: undefined,
}
},
methods: {
showContextMenu() {
console.log('will show context menu.')
this.contextMenuVisible = true;
},
hideContextMenu() {
this.contextMenuHideTimeout = setTimeout( () => {
this.contextMenuVisible = false;
}, 250);
}
}
}
</script>
<style>
.context-container {
position: relative;
}
.context-menu-wrapper {
position: relative;
}
.context-menu {
position: absolute;
display: flex;
flex-direction: column;
min-width: 5rem;
top: 50%;
transform: translateY(-50%);
}
.menu-left {
right: 100%;
}
.menu-right {
left: 100%;
}
</style>

View File

@ -89,14 +89,8 @@ const ExtensionConf: SettingsInterface = {
thresholdStep: 8, // when failing to set aspect ratio, increase threshold by this much
increaseAfterConsecutiveResets: 2 // increase if AR resets this many times in a row
},
blackLevels: {
defaultBlack: 16,
blackTolerance: 4,
imageDelta: 16,
},
sampling: {
edgePosition: 0.25,
staticCols: 16, // we take a column at [0-n]/n-th parts along the width and sample it
staticCols: 9, // 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
},
@ -109,25 +103,16 @@ const ExtensionConf: SettingsInterface = {
edgeTolerancePx: 2, // black edge violation is performed this far from reported 'last black pixel'
edgeTolerancePercent: null // unused. same as above, except use % of canvas height instead of pixels
},
fallbackMode: {
enabled: true,
safetyBorderPx: 5, // determines the thickness of safety border in fallback mode
noTriggerZonePx: 8 // if we detect edge less than this many pixels thick, we don't correct.
},
arSwitchLimiter: { // to be implemented
switches: 2, // we can switch this many times
period: 2.0 // per this period
},
edgeDetection: {
slopeTestWidth: 8,
gradientTestSamples: 8,
gradientTestBlackThreshold: 16,
gradientTestDeltaThreshold: 32,
gradientTestMinDelta: 8,
thresholds: {
edgeDetectionLimit: 8,
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, //

View File

@ -8,7 +8,7 @@ import { GlCanvas } from './gl/GlCanvas';
import { AardCanvasStore } from './interfaces/aard-canvas-store.interface';
import { AardDetectionSample, generateSampleArray } from './interfaces/aard-detection-sample.interface';
import { AardStatus, initAardStatus } from './interfaces/aard-status.interface';
import { AardTestResults, initAardTestResults, resetAardTestResults } from './interfaces/aard-test-results.interface';
import { AardTestResults, initAardTestResults } from './interfaces/aard-test-results.interface';
import { AardTimers, initAardTimers } from './interfaces/aard-timers.interface';
@ -214,7 +214,7 @@ class Aard {
//#region configuration parameters
private logger: Logger;
private videoData: VideoData;
private conf: VideoData;
private settings: Settings;
private eventBus: EventBus;
private arid: string;
@ -256,7 +256,7 @@ class Aard {
//#region lifecycle
constructor(videoData: VideoData){
this.logger = videoData.logger;
this.videoData = videoData;
this.conf = videoData;
this.video = videoData.video;
this.settings = videoData.settings;
this.eventBus = videoData.eventBus;
@ -284,10 +284,6 @@ class Aard {
}
}
/**
* Initializes Aard with default values and starts autodetection loop.
* This method should only ever be called from constructor.
*/
private init() {
this.canvasStore = {
main: new GlCanvas(new GlCanvas(this.settings.active.arDetect.canvasDimensions.sampleCanvas)),
@ -304,17 +300,16 @@ class Aard {
),
};
this.start();
}
//#endregion
/**
* Starts autodetection loop.
*/
//#endregion
start() {
if (this.videoData.resizer.lastAr.type === AspectRatioType.AutomaticUpdate) {
if (this.conf.resizer.lastAr.type === AspectRatioType.AutomaticUpdate) {
// ensure first autodetection will run in any case
this.videoData.resizer.lastAr = {type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr};
this.conf.resizer.lastAr = {type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr};
}
if (this.animationFrame) {
@ -323,63 +318,15 @@ class Aard {
this.status.aardActive = true;
this.animationFrame = window.requestAnimationFrame( (ts: DOMHighResTimeStamp) => this.onAnimationFrame(ts));
}
/**
* Runs autodetection ONCE.
* If autodetection loop is running, this will also stop autodetection loop.
*/
step() {
this.stop();
this.main();
}
/**
* Stops autodetection.
*/
stop() {
if (this.animationFrame) {
window.cancelAnimationFrame(this.animationFrame);
}
}
//#region animationFrame, scheduling, and other shit
/**
* Checks whether conditions for granting a frame check are fulfilled
* @returns
*/
private canTriggerFrameCheck() {
// if (this._paused || this._halted || this._exited) {
// return false;
// }
// if video was paused & we know that we already checked that frame,
// we will not check it again.
const videoState = this.getVideoPlaybackState();
if (videoState !== VideoPlaybackState.Playing) {
if (this.status.lastVideoStatus === videoState) {
return false;
}
}
this.status.lastVideoStatus = videoState;
if (Date.now() < this.timers.nextFrameCheckTime) {
return false;
}
this.timers.nextFrameCheckTime = Date.now() + this.settings.active.arDetect.timers.playing;
return true;
// this.logger.log('info', 'debug', `"%c[ArDetect::startLoop] <@${this.arid}> AARD loop started.`, _ard_console_start);
}
private onAnimationFrame(ts: DOMHighResTimeStamp) {
if (this.canTriggerFrameCheck()) {
resetAardTestResults(this.testResults);
this.main();
}
this.animationFrame = window.requestAnimationFrame( (ts: DOMHighResTimeStamp) => this.onAnimationFrame(ts));
}
//#endregion
/**
* Main loop for scanning aspect ratio changes
@ -443,33 +390,42 @@ class Aard {
} while (false);
// TODO: subtitle check goes here.
// Note that subtitle check should reset aspect ratio outright, regardless of what other tests revealed.
// Also note that subtitle check should run on newest aspect ratio data, rather than lag one frame behind
// But implementation details are something for future Tam to figure out
// if detection is uncertain, we don't do anything at all
if (this.testResults.aspectRatioUncertain) {
return;
}
// TODO: emit debug values if debugging is enabled
this.testResults.isFinished = true;
// if edge width changed, emit update event.
if (this.testResults.aspectRatioUpdated) {
this.videoData.resizer.updateAr({
type: AspectRatioType.AutomaticUpdate,
ratio: this.getAr(),
offset: this.testResults.letterboxOffset
});
}
} catch (e) {
console.warn('[Ultrawidify] Aspect ratio autodetection crashed for some reason.\n\nsome reason:', e);
this.videoData.resizer.setAr({type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr});
}
}
/**
* Checks whether conditions for granting a frame check are fulfilled
* @returns
*/
private canTriggerFrameCheck() {
// if (this._paused || this._halted || this._exited) {
// return false;
// }
// if video was paused & we know that we already checked that frame,
// we will not check it again.
const videoState = this.getVideoPlaybackState();
if (videoState !== VideoPlaybackState.Playing) {
if (this.status.lastVideoStatus === videoState) {
return false;
}
}
this.status.lastVideoStatus = videoState;
if (Date.now() < this.timers.nextFrameCheckTime) {
return false;
}
this.timers.nextFrameCheckTime = Date.now() + this.settings.active.arDetect.timers.playing;
return true;
}
private getVideoPlaybackState(): VideoPlaybackState {
try {
if (this.video.ended) {
@ -1612,22 +1568,7 @@ class Aard {
this.testResults.aspectRatioUncertain = false;
this.testResults.letterboxWidth = candidateAvg;
this.testResults.letterboxOffset = diff;
this.testResults.aspectRatioUpdated = true;
}
/**
* Calculates video's current aspect ratio based on data in testResults.
* @returns
*/
private getAr() {
const fileAr = this.video.videoWidth / this.video.videoHeight;
const canvasAr = this.canvasStore.main.width / this.canvasStore.main.height;
const compensatedWidth = fileAr === canvasAr ? this.canvasStore.main.width : this.canvasStore.main.width * fileAr;
return compensatedWidth / (this.canvasStore.main.height - (this.testResults.letterboxWidth * 2));
}
//#endregion
}

View File

@ -37,7 +37,7 @@ export function resetGradientSamples(samples: AardGradientSamples) {
}
}
if (samples.left) {
for (let i = 0; i < samples.left.length; i++) {
for (let i = 0; i < samples.left.length, i++) {
for (let j = 0; j < samples.left[i].length; j++) {
samples.left[i][j] = 0;
}

View File

@ -28,7 +28,6 @@ export interface AardTestResults {
bottomCandidateQuality: number,
},
aspectRatioUncertain: boolean,
aspectRatioUpdated: boolean,
letterboxWidth: number,
letterboxOffset: number,
logoDetected: [boolean, boolean, boolean, boolean]
@ -62,11 +61,10 @@ export function initAardTestResults(settings: AardSettings): AardTestResults {
bottomCandidate: 0,
bottomCandidateQuality: 0,
},
aspectRatioUncertain: false,
aspectRatioUpdated: false,
letterboxWidth: 0,
letterboxOffset: 0,
logoDetected: [false, false, false, false]
}
}
@ -79,7 +77,4 @@ export function resetAardTestResults(results: AardTestResults): void {
results.guardLine.cornerViolations[1] = false;
results.guardLine.cornerViolations[2] = false;
results.guardLine.cornerViolations[3] = false;
results.letterboxWidth = 0;
results.letterboxOffset = 0;
results.aspectRatioUpdated = false;
}

View File

@ -709,12 +709,39 @@ class VideoData {
//#endregion
//#region shit that gets propagated to resizer and should be removed. Implement an event bus instead
panHandler(event, forcePan?: boolean) {
if (this.invalid) {
return;
}
if(this.destroyed) {
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
return;
}
if(!this.resizer) {
this.destroy();
return;
}
this.resizer.panHandler(event, forcePan);
}
setPanMode(mode) {
if (this.invalid) {
return;
}
this.resizer.setPanMode(mode);
}
restoreAr(){
if (this.invalid) {
return;
}
this.resizer.restore();
}
isPlaying() {
return this.video && this.video.currentTime > 0 && !this.video.paused && !this.video.ended;
}
//#endregion
checkVideoSizeChange(){