Refactor playerData/player size changing a bit. Settings can now only be patched from ExtConfPatches

This commit is contained in:
Tamius Han 2025-10-14 20:26:35 +02:00
parent baa7c79f6c
commit 095a3daadd
11 changed files with 247 additions and 274 deletions

2
package-lock.json generated
View File

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

View File

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

@ -165,7 +165,7 @@
<div class="label">Subtitle detection:</div> <div class="label">Subtitle detection:</div>
<div class="select"> <div class="select">
<select v-model="aardSettings.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>
<option :value="AardSubtitleCropMode.CropSubtitles">Always crop subtitles</option> <option :value="AardSubtitleCropMode.CropSubtitles">Always crop subtitles</option>

View File

@ -97,6 +97,8 @@ class Logger {
} }
static saveConfig(conf: LoggerConfig) { static saveConfig(conf: LoggerConfig) {
console.warn('LEGACY LOGGER IS STILL BEING CALLED FROM SOMEWHERE!', new Error().stack);
if (process.env.CHANNEL === 'dev') { if (process.env.CHANNEL === 'dev') {
console.info('Saving logger conf:', conf) console.info('Saving logger conf:', conf)
} }

View File

@ -954,7 +954,7 @@ export class Aard {
searchRow += scanSpacing searchRow += scanSpacing
) { ) {
if (++outerIteration > height) { if (++outerIteration > height) {
console.warn('[ultrawidify|aard::subtitleScanRegionLinear] — scan got stuck in an infinite loop. This shouldn\'t happen.'); // console.warn('[ultrawidify|aard::subtitleScanRegionLinear] — scan got stuck in an infinite loop. This shouldn\'t happen.');
results.uncertain; results.uncertain;
break outerLoop; break outerLoop;
} }

View File

@ -113,7 +113,7 @@ export class LogAggregator {
private storageChangeListener(changes, area) { private storageChangeListener(changes, area) {
if (!changes[STORAGE_LOG_SETTINGS_KEY]) { if (!changes[STORAGE_LOG_SETTINGS_KEY]) {
console.info('We dont have any logging settings, not processing frther', changes); // console.info('We dont have any logging settings, not processing frther', changes);
return; return;
} }

View File

@ -213,6 +213,8 @@ class Settings {
} }
); );
// this.active.newFeatureTracker = {};
// apply all remaining patches // apply all remaining patches
this.logger?.info('applySettingsPatches', `There are ${ExtensionConfPatch.length - index} settings patches to apply`); this.logger?.info('applySettingsPatches', `There are ${ExtensionConfPatch.length - index} settings patches to apply`);
@ -288,19 +290,6 @@ class Settings {
return this.active; return this.active;
} }
// This means extension update happened.
// btw fun fact — we can do version rollbacks, which might come in handy while testing
this.active.version = this.version;
// if extension has been updated, update existing settings with any options added in the
// new version. In addition to that, we remove old keys that are no longer used.
const patched = ObjectCopy.addNew(settings, this.default);
this.logger?.info('init',"Results from ObjectCopy.addNew()?", patched, "\n\nSettings from storage", settings, "\ndefault?", this.default);
if (patched) {
this.active = patched;
}
// in case settings in previous version contained a fucky wucky, we overwrite existing settings with a patch // in case settings in previous version contained a fucky wucky, we overwrite existing settings with a patch
this.applySettingsPatches(oldVersion); this.applySettingsPatches(oldVersion);

View File

@ -11,6 +11,7 @@ import PageInfo from './PageInfo';
import { RunLevel } from '../../enum/run-level.enum'; import { RunLevel } from '../../enum/run-level.enum';
import { ExtensionEnvironment } from '../../../common/interfaces/SettingsInterface'; import { ExtensionEnvironment } from '../../../common/interfaces/SettingsInterface';
import { ComponentLogger } from '../logging/ComponentLogger'; import { ComponentLogger } from '../logging/ComponentLogger';
import { collectionHas, equalish } from '../../util/comparators';
if (process.env.CHANNEL !== 'stable'){ if (process.env.CHANNEL !== 'stable'){
console.info("Loading: PlayerData.js"); console.info("Loading: PlayerData.js");
@ -96,6 +97,7 @@ class PlayerData {
isTooSmall: boolean = true; isTooSmall: boolean = true;
//#endregion //#endregion
//#region misc stuff //#region misc stuff
extensionMode: any; extensionMode: any;
dimensions: PlayerDimensions; dimensions: PlayerDimensions;
@ -144,11 +146,12 @@ class PlayerData {
private dimensionChangeListener = { private dimensionChangeListener = {
that: this, that: this,
handleEvent: function(event: Event) { handleEvent: function(event: Event) {
this.that.trackEnvironmentChanges(event); this.that.requestTick();
this.that.trackDimensionChanges()
} }
} }
private fallbackDimensionChangeInterval: any;
/** /**
* Gets player aspect ratio. If in full screen, it returns screen aspect ratio unless settings say otherwise. * Gets player aspect ratio. If in full screen, it returns screen aspect ratio unless settings say otherwise.
*/ */
@ -158,8 +161,7 @@ class PlayerData {
return window.innerWidth / window.innerHeight; return window.innerWidth / window.innerHeight;
} }
if (!this.dimensions) { if (!this.dimensions) {
this.trackDimensionChanges(); return (this.element?.scrollWidth ?? this.videoElement.videoWidth) / (this.element?.scrollHeight ?? this.videoElement.videoHeight);
this.trackEnvironmentChanges();
} }
return this.dimensions.width / this.dimensions.height; return this.dimensions.width / this.dimensions.height;
@ -225,7 +227,6 @@ class PlayerData {
} }
this.startChangeDetection(); this.startChangeDetection();
document.addEventListener('fullscreenchange', this.dimensionChangeListener); document.addEventListener('fullscreenchange', this.dimensionChangeListener);
// we want to reload on storage changes // we want to reload on storage changes
@ -236,35 +237,6 @@ class PlayerData {
} }
} }
/**
* Checks if player dimensions are too small for autodetection to run.
* Full screen is never too small, even if it's
* @param playerDimensions
* @returns
*/
private checkIfTooSmall(playerDimensions?: PlayerDimensions) {
if (playerDimensions) {
return !playerDimensions.fullscreen && (playerDimensions.width < 1208 || playerDimensions.height < 720);
} else {
return !document.fullscreenElement && (this.element.clientWidth < 1208 || this.element.clientHeight < 720);
}
}
private reloadPlayerDataConfig(siteConfUpdate) {
// this.siteSettings = siteConfUpdate;
this.updatePlayer();
this.periodicallyRefreshPlayerElement = false;
try {
this.periodicallyRefreshPlayerElement = this.siteSettings.data.currentDOMConfig.periodicallyRefreshPlayerElement;
} catch (e) {
// no biggie — that means we don't have any special settings for this site.
}
// because this is often caused by the UI
this.handlePlayerTreeRequest();
}
/** /**
* Initializes event bus * Initializes event bus
*/ */
@ -276,18 +248,27 @@ class PlayerData {
} }
} }
/** private initFallbackDimensionMonitor() {
* Completely stops everything the extension is doing this.stopFallbackDimensionMonitor();
*/ this.fallbackDimensionChangeInterval = setInterval( () => {
destroy() { this.processTick();
document.removeEventListener('fullscreenchange', this.dimensionChangeListener); }, 2000);
this.stopChangeDetection();
this.ui?.destroy();
this.notificationService?.destroy();
} }
//#endregion
deferredUiInitialization(playerDimensions) { /**
* Stops manually scanning for dimension changes
*/
stopFallbackDimensionMonitor() {
clearImmediate(this.fallbackDimensionChangeInterval);
this.fallbackDimensionChangeInterval = undefined;
}
/**
* Initialized player UI at a later time.
* @param playerDimensions
* @returns
*/
deferredUiInitialization(playerDimensions = this.dimensions) {
if (this.ui || this.siteSettings.data.enableUI.fullscreen === ExtensionMode.Disabled) { if (this.ui || this.siteSettings.data.enableUI.fullscreen === ExtensionMode.Disabled) {
return; return;
} }
@ -325,6 +306,44 @@ class PlayerData {
} }
} }
/**
* Completely stops everything the extension is doing
*/
destroy() {
document.removeEventListener('fullscreenchange', this.dimensionChangeListener);
this.stopChangeDetection();
this.ui?.destroy();
this.notificationService?.destroy();
}
//#endregion
//#region utils
/**
* Checks if player dimensions are too small for autodetection to run.
* Full screen is never too small, even if it's
* @param playerDimensions
* @returns
*/
private checkIfTooSmall() {
return !document.fullscreenElement && (this.element.clientWidth < 1208 || this.element.clientHeight < 720);
}
private reloadPlayerDataConfig(siteConfUpdate) {
// this.siteSettings = siteConfUpdate;
this.updatePlayer();
this.periodicallyRefreshPlayerElement = false;
try {
this.periodicallyRefreshPlayerElement = this.siteSettings.data.currentDOMConfig.periodicallyRefreshPlayerElement;
} catch (e) {
// no biggie — that means we don't have any special settings for this site.
}
// because this is often caused by the UI
this.handlePlayerTreeRequest();
}
/** /**
* Sets extension runLevel and sets or unsets appropriate css classes as necessary * Sets extension runLevel and sets or unsets appropriate css classes as necessary
* @param runLevel * @param runLevel
@ -356,161 +375,12 @@ class PlayerData {
this.runLevel = runLevel; this.runLevel = runLevel;
} }
/** //#endregion
* Detects whether player element is in theater mode or not.
* If theater mode changed, emits event.
* @returns whether player is in theater mode
*/
private detectTheaterMode() {
const oldTheaterMode = this.isTheaterMode;
const newTheaterMode = this.equalish(window.innerWidth, this.element.offsetWidth, 32);
this.isTheaterMode = newTheaterMode;
// theater mode changed
if (oldTheaterMode !== newTheaterMode) {
if (newTheaterMode) {
this.eventBus.send('player-theater-enter', {});
} else {
this.eventBus.send('player-theater-exit', {});
}
}
return newTheaterMode;
}
trackEnvironmentChanges() {
if (this.environment !== this.lastEnvironment) {
this.lastEnvironment = this.environment;
this.eventBus.send('uw-environment-change', {newEnvironment: this.environment});
}
}
/**
*
*/
trackDimensionChanges() {
if (this._isTrackDimensionChangesActive) {
// this shouldn't really get called, _ever_ ... but sometimes it happens
console.warn('[PlayerData::trackDimensionChanges] trackDimensionChanges is already active!');
return;
}
this._isTrackDimensionChangesActive = true;
try {
// get player dimensions _once_
let currentPlayerDimensions;
let fsChanged = this.isFullscreen !== !!document.fullscreenElement;
this.isFullscreen = !!document.fullscreenElement;
if (this.isFullscreen) {
currentPlayerDimensions = {
width: window.innerWidth,
height: window.innerHeight,
};
} else {
currentPlayerDimensions = {
width: this.element.offsetWidth,
height: this.element.offsetHeight
};
this.detectTheaterMode();
}
// defer creating UI
this.deferredUiInitialization(currentPlayerDimensions);
// if dimensions of the player box are the same as the last known
// dimensions, we don't have to do anything ... in theory. In practice,
// sometimes restore-ar doesn't appear to register the first time, and
// this function doesn't really run often enough to warrant finding a
// real, optimized fix.
if (
this.dimensions?.width == currentPlayerDimensions.width
&& this.dimensions?.height == currentPlayerDimensions.height
) {
this.eventBus.send('restore-ar', null);
this.eventBus.send('delayed-restore-ar', {delay: 500});
this.dimensions = currentPlayerDimensions;
this._isTrackDimensionChangesActive = false;
return;
}
// in every other case, we need to check if the player is still
// big enough to warrant our extension running.
this.handleSizeConstraints(currentPlayerDimensions);
// this.handleDimensionChanges(currentPlayerDimensions, this.dimensions);
// Save current dimensions to avoid triggering this function pointlessly
this.dimensions = currentPlayerDimensions;
if (fsChanged) {
this.updatePlayer();
}
} catch (e) {
}
this._isTrackDimensionChangesActive = false;
}
/**
* Checks if extension is allowed to run in current environment.
* @param currentPlayerDimensions
*/
private handleSizeConstraints(currentPlayerDimensions: PlayerDimensions) {
// Check if extension is allowed to run in current combination of theater + full screen
const canEnable = this.siteSettings.isEnabledForEnvironment(this.isFullscreen, this.isTheaterMode) === ExtensionMode.Enabled;
if (this.runLevel === RunLevel.Off && canEnable) {
this.eventBus.send('restore-ar', null);
// must be called after
this.handleDimensionChanges(currentPlayerDimensions, this.dimensions);
} else if (!canEnable && this.runLevel !== RunLevel.Off) {
// must be called before
this.handleDimensionChanges(currentPlayerDimensions, this.dimensions);
this.setRunLevel(RunLevel.Off);
}
}
private handleDimensionChanges(newDimensions: PlayerDimensions, oldDimensions: PlayerDimensions) {
if (this.runLevel === RunLevel.Off ) {
this.logger.info('handleDimensionChanges', "player size changed, but PlayerDetect is in disabled state. The player element is probably too small.");
return;
}
// this 'if' is just here for debugging — real code starts later. It's safe to collapse and
// ignore the contents of this if (unless we need to change how logging works)
this.logger.info('handleDimensionChanges', "player size potentially changed.\n\nold dimensions:", oldDimensions, '\nnew dimensions:', newDimensions);
// if size doesn't match, trigger onPlayerDimensionChange
if (
newDimensions?.width != oldDimensions?.width
|| newDimensions?.height != oldDimensions?.height
|| newDimensions?.fullscreen != oldDimensions?.fullscreen
){
// If player size changes, we restore aspect ratio
this.eventBus.send('restore-ar', null);
this.eventBus.send('delayed-restore-ar', {delay: 500});
// this.videoData.resizer?.restore();
this.eventBus.send('uw-config-broadcast', {
type: 'player-dimensions',
data: newDimensions
});
this.isTooSmall = this.checkIfTooSmall(newDimensions);
}
}
onPlayerDimensionsChanged: ResizeObserverCallback = _.debounce( onPlayerDimensionsChanged: ResizeObserverCallback = _.debounce(
(mutationList?, observer?) => { (mutationList?, observer?) => {
this.trackDimensionChanges(); this._requestTick(true);
this.trackEnvironmentChanges();
}, },
250, // do it once per this many ms 250, // do it once per this many ms
{ {
@ -519,6 +389,112 @@ class PlayerData {
} }
); );
private _requestTick(immediately?: boolean) {
if (immediately) {
this.processTick();
}
}
// Currently just a dummy, but at some point we could try to avoid running
// processTick on every tick — instead, running it only when needed.
// But today is not that day.
requestTick() {
this._requestTick();
}
/**
* Checks if anything changed
* @returns
*/
private processTick() {
let changeDetected = false;
let changes = {
player: false,
fullScreen: false,
theater: false,
dimensions: false,
}
let fs, newPlayerCandidate, isTheaterMode, currentDimensions;
fullScreenCheck:
{
fs = !!document.fullscreenElement;
if (fs !== this.isFullscreen) {
changes.fullScreen = true;
changeDetected = true;
}
}
playerCheck:
{
newPlayerCandidate = this.getPlayer();
if (!newPlayerCandidate) {
console.warn('[uw->PlayerData::processTick()] — player not detected — dimensions will not be checked')
return;
}
if (newPlayerCandidate !== this.element) {
changes.player = true;
changeDetected = true;
}
}
theaterModeCheck:
{
isTheaterMode = equalish(window.innerWidth, this.element.offsetWidth, 32);
if (isTheaterMode !== this.isTheaterMode) {
changes.theater = true;
changeDetected = true;
}
}
dimensionCheck:
{
if (this.isFullscreen) {
currentDimensions = {
width: window.innerWidth,
height: window.innerHeight,
};
} else {
currentDimensions = {
width: this.element.offsetWidth,
height: this.element.offsetHeight
};
}
if (currentDimensions.width !== this.dimensions?.width || currentDimensions.height !== this.dimensions?.height) {
changes.dimensions = true;
changeDetected = true;
}
}
// we only commit changes if no errors were encountered
commitChanges:
{
if (changes.fullScreen) this.isFullscreen = fs;
if (changes.player) this.element = newPlayerCandidate;
if (changes.theater) this.isTheaterMode = isTheaterMode;
if (changes.dimensions) this.dimensions = currentDimensions;
this.isTooSmall = this.checkIfTooSmall();
}
const canEnable = this.siteSettings.isEnabledForEnvironment(this.isFullscreen, this.isTheaterMode) === ExtensionMode.Enabled;
if (this.runLevel === RunLevel.Off && canEnable) {
this.eventBus.send('restore-ar', null);
} else if (!canEnable && this.runLevel !== RunLevel.Off) {
// must be called before
this.eventBus.send('restore-ar', null);
this.setRunLevel(RunLevel.Off);
}
if (changeDetected) {
this.deferredUiInitialization();
this.eventBus.send('restore-ar', null);
this.eventBus.send('delayed-restore-ar', {delay: 500});
}
}
//#region player element change detection //#region player element change detection
/** /**
@ -546,46 +522,13 @@ class PlayerData {
// legacy mode still exists, but acts as a fallback for observers and is triggered less // legacy mode still exists, but acts as a fallback for observers and is triggered less
// frequently in order to avoid too many pointless checks // frequently in order to avoid too many pointless checks
this.legacyChangeDetection(); this.initFallbackDimensionMonitor();
}
async legacyChangeDetection() {
while (!this.halted) {
await sleep(1000);
try {
this.forceRefreshPlayerElement();
} catch (e) {
console.error('[PlayerData::legacycd] this message is pretty high on the list of messages you shouldn\'t see', e);
}
}
}
doPeriodicPlayerElementChangeCheck() {
if (this.periodicallyRefreshPlayerElement) {
this.forceRefreshPlayerElement();
}
} }
stopChangeDetection(){ stopChangeDetection(){
this.observer.disconnect(); this.observer.disconnect();
} }
//#region helper functions
collectionHas(collection, element) {
for (let i = 0, len = collection.length; i < len; i++) {
if (collection[i] == element) {
return true;
}
}
return false;
}
equalish(a,b, tolerance) {
return a > b - tolerance && a < b + tolerance;
}
//#endregion
private getElementStack(): ElementStack { private getElementStack(): ElementStack {
const elementStack: ElementStack = [{ const elementStack: ElementStack = [{
element: this.videoElement, element: this.videoElement,
@ -617,19 +560,22 @@ class PlayerData {
return this.elementStack; return this.elementStack;
} }
updatePlayer(options?: {verbose?: boolean, newElement?: HTMLElement}) { updatePlayer(options?: {verbose?: boolean, newElement?: HTMLElement}) {
const newPlayer = options?.newElement ?? this.getPlayer(options); const newPlayer = options?.newElement ?? this.getPlayer(options);
if (newPlayer === this.element) { if (newPlayer === this.element || !newPlayer) {
return; return;
} }
this.observer?.unobserve(this.element);
// clean up and re-initialize UI // clean up and re-initialize UI
this.ui?.destroy(); this.ui?.destroy();
delete this.ui; delete this.ui;
// Don't forget to also move observer to the new player
// (if observer exists)
this.element = newPlayer; this.element = newPlayer;
this.observer?.observe(this.element);
this.ui = new UI( this.ui = new UI(
'ultrawidifyUi', 'ultrawidifyUi',
@ -642,8 +588,7 @@ class PlayerData {
} }
); );
this.trackDimensionChanges(); this.requestTick();
this.trackEnvironmentChanges();
} }
/** /**
@ -695,8 +640,12 @@ class PlayerData {
return playerCandidate.element; return playerCandidate.element;
} else { } else {
const playerCandidate = this.getPlayerAuto(elementStack, videoWidth, videoHeight); const playerCandidate = this.getPlayerAuto(elementStack, videoWidth, videoHeight);
playerCandidate.heuristics['activePlayer'] = true; if (playerCandidate === null) {
return playerCandidate.element; console.warn('[uw::getPlayer] getPlayerAuto returned null — no player detected?');
} else {
playerCandidate.heuristics['activePlayer'] = true;
return playerCandidate.element;
}
} }
} }
@ -713,8 +662,8 @@ class PlayerData {
const elementStack = this.getElementStack(); const elementStack = this.getElementStack();
if ( if (
this.equalish(elementStack[currentIndex].element.offsetWidth, elementStack[nextIndex].element.offsetWidth, 2) equalish(elementStack[currentIndex].element.offsetWidth, elementStack[nextIndex].element.offsetWidth, 2)
&& this.equalish(elementStack[currentIndex].element.offsetHeight, elementStack[nextIndex].element.offsetHeight, 2) && equalish(elementStack[currentIndex].element.offsetHeight, elementStack[nextIndex].element.offsetHeight, 2)
) { ) {
// this.siteSettings.set('playerAutoConfig.initialIndex', this.siteSettings.data.playerAutoConfig.initialIndex + 1, {noSave: true}); // this.siteSettings.set('playerAutoConfig.initialIndex', this.siteSettings.data.playerAutoConfig.initialIndex + 1, {noSave: true});
// this.siteSettings.set('playerAutoConfig.modified', true); // this.siteSettings.set('playerAutoConfig.modified', true);
@ -765,8 +714,9 @@ class PlayerData {
// Don't bother thinking about this too much, as any "thinking" was quickly // Don't bother thinking about this too much, as any "thinking" was quickly
// corrected by bugs caused by various edge cases. // corrected by bugs caused by various edge cases.
if ( if (
this.equalish(elementData.height, videoHeight, 5)
|| this.equalish(elementData.width, videoWidth, 5) equalish(elementData.height, videoHeight, 5)
|| equalish(elementData.width, videoWidth, 5)
) { ) {
let score = 1000; let score = 1000;
@ -877,14 +827,14 @@ class PlayerData {
const allSelectors = document.querySelectorAll(queryString); const allSelectors = document.querySelectorAll(queryString);
for (const element of elementStack) { for (const element of elementStack) {
if (this.collectionHas(allSelectors, element.element)) { if (collectionHas(allSelectors, element.element)) {
let score = 100; let score = 100;
// we award points to elements which match video size in one // we award points to elements which match video size in one
// dimension and exceed it in the other // dimension and exceed it in the other
if ( if (
(element.width >= videoWidth && this.equalish(element.height, videoHeight, 2)) (element.width >= videoWidth && equalish(element.height, videoHeight, 2))
|| (element.height >= videoHeight && this.equalish(element.width, videoWidth, 2)) || (element.height >= videoHeight && equalish(element.width, videoWidth, 2))
) { ) {
score += 75; score += 75;
} }
@ -917,8 +867,6 @@ class PlayerData {
private handlePlayerTreeRequest() { private handlePlayerTreeRequest() {
// this populates this.elementStack fully // this populates this.elementStack fully
this.updatePlayer({verbose: true}); this.updatePlayer({verbose: true});
console.log('tree:', JSON.parse(JSON.stringify(this.elementStack)));
console.log('————————————————————— handling player tree request!')
this.eventBus.send('uw-config-broadcast', {type: 'player-tree', config: JSON.parse(JSON.stringify(this.elementStack))}); this.eventBus.send('uw-config-broadcast', {type: 'player-tree', config: JSON.parse(JSON.stringify(this.elementStack))});
} }
@ -933,7 +881,7 @@ class PlayerData {
this.markedElement.remove(); this.markedElement.remove();
} }
const elementBB = this.elementStack[data.parentIndex].element.getBoundingClientRect(); const elementBB = this.elementStack[data.parentIndex].element.getBoundingClientRect();;
// console.log('element bounding box:', elementBB); // console.log('element bounding box:', elementBB);

View File

@ -65,6 +65,8 @@ class Resizer {
private effectiveZoom: {x: number, y: number} = {x: 1, y: 1}; private effectiveZoom: {x: number, y: number} = {x: 1, y: 1};
private pendingAr?: {ar: Ar, lastAr?: Ar};
_lastAr: Ar = {type: AspectRatioType.Initial}; _lastAr: Ar = {type: AspectRatioType.Initial};
set lastAr(x: Ar) { set lastAr(x: Ar) {
// emit updates for UI when setting lastAr, but only if AR really changed // emit updates for UI when setting lastAr, but only if AR really changed
@ -332,6 +334,19 @@ class Resizer {
return; return;
} }
// If we are missing some data that's necessary for crop calculations,
// set pendingAr and quit
if (!this.videoData.player.dimensions?.width || !this.videoData.player.dimensions?.height) {
this.pendingAr = {ar, lastAr};
this.videoData.player.requestTick();
return;
}
this.pendingAr = null;
if (! this.videoData.player.dimensions) {
this.videoData.player.updatePlayer();
}
// If no aspect ratio is applied AND if no stretch mode is active, // If no aspect ratio is applied AND if no stretch mode is active,
// we disable our CSS in order to prevent breaking websites by default, // we disable our CSS in order to prevent breaking websites by default,
// without any human interaction // without any human interaction
@ -465,8 +480,12 @@ class Resizer {
// this.videoData.eventBus.send('announce-zoom', this.manualZoom ? {x: this.zoom.scale, y: this.zoom.scaleY} : this.zoom.effectiveZoom); // this.videoData.eventBus.send('announce-zoom', this.manualZoom ? {x: this.zoom.scale, y: this.zoom.scaleY} : this.zoom.effectiveZoom);
// } // }
let translate = this.computeOffsets(stretchFactors, options?.ar); try {
this.applyCss(stretchFactors, translate); const translate = this.computeOffsets(stretchFactors, options?.ar);
this.applyCss(stretchFactors, translate);
} catch (e) {
// don't apply CSS if there's an error
}
} }
toFixedAr() { toFixedAr() {
@ -567,6 +586,10 @@ class Resizer {
* @returns * @returns
*/ */
restore() { restore() {
if (this.pendingAr) {
this.setAr(this.pendingAr.ar, this.pendingAr.lastAr);
return;
}
if (!this.manualZoom) { if (!this.manualZoom) {
this.logger.info('restore', `<rid:${this.resizerId}> attempting to restore aspect ratio`, {'lastAr': this.lastAr} ); this.logger.info('restore', `<rid:${this.resizerId}> attempting to restore aspect ratio`, {'lastAr': this.lastAr} );
@ -858,16 +881,15 @@ class Resizer {
) { ) {
this.logger.warn('computeOffsets', `<rid:${this.resizerId}> We are getting some incredibly funny results here.\n\n`, this.logger.warn('computeOffsets', `<rid:${this.resizerId}> We are getting some incredibly funny results here.\n\n`,
`Video seems to be both wider and taller (or shorter and narrower) than player element at the same time. This is super duper not supposed to happen.\n\n`, `Video seems to be both wider and taller (or shorter and narrower) than player element at the same time. This is super duper not supposed to happen.\n\n`,
`Something is probably undefined:\n`,
`\n videoData.video offset w x h:`, this.videoData.video.offsetWidth, 'x', this.videoData.video.offsetHeight,
`\n videoData.player.dimensions w x h:`, this.videoData.player.dimensions.width, 'x', this.videoData.player.dimensions.height,
`Player element needs to be checked.` `Player element needs to be checked.`
); );
// request dimension change tick.
// sometimes this appears to randomly recurse. // this will cause
// There seems to be no way to reproduce it. this.videoData.player.requestTick();
if (! this._computeOffsetsRecursionGuard) { throw 'DIMENSIONS_ERROR';
this._computeOffsetsRecursionGuard = true;
this.videoData.player.trackDimensionChanges();
this._computeOffsetsRecursionGuard = false;
}
} }
return translate; return translate;

View File

@ -0,0 +1,12 @@
export function collectionHas(collection, element): boolean {
for (let i = 0, len = collection.length; i < len; i++) {
if (collection[i] == element) {
return true;
}
}
return false;
}
export function equalish(a: number,b: number, tolerance: number): boolean {
return a > b - tolerance && a < b + tolerance;
}

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.990", "version": "6.3.993",
"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"