diff --git a/src/common/interfaces/ArInterface.ts b/src/common/interfaces/ArInterface.ts
index d9e25ed..0a2b91c 100644
--- a/src/common/interfaces/ArInterface.ts
+++ b/src/common/interfaces/ArInterface.ts
@@ -1,6 +1,12 @@
import AspectRatioType from '../enums/AspectRatioType.enum';
+export enum ArVariant {
+ Crop = undefined,
+ Zoom = 1
+}
+
export interface Ar {
type: AspectRatioType,
- ratio?: number
+ ratio?: number,
+ variant?: ArVariant
}
diff --git a/src/csui/src/popup/panels/PopupVideoSettings.vue b/src/csui/src/popup/panels/PopupVideoSettings.vue
index a986747..d993f10 100644
--- a/src/csui/src/popup/panels/PopupVideoSettings.vue
+++ b/src/csui/src/popup/panels/PopupVideoSettings.vue
@@ -43,6 +43,14 @@
Zoom:
+
+
+
diff --git a/src/ext/lib/aard/Aard.ts b/src/ext/lib/aard/Aard.ts
index 63eee00..313082f 100644
--- a/src/ext/lib/aard/Aard.ts
+++ b/src/ext/lib/aard/Aard.ts
@@ -1,6 +1,7 @@
-import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
-import ExtensionMode from '../../../common/enums/ExtensionMode.enum';
-import { ExtensionEnvironment } from '../../../common/interfaces/SettingsInterface';
+import AspectRatioType from '@src/common/enums/AspectRatioType.enum';
+import ExtensionMode from '@src/common/enums/ExtensionMode.enum';
+import { ArVariant } from '@src/common/interfaces/ArInterface';
+import { ExtensionEnvironment } from '@src/common/interfaces/SettingsInterface';
import EventBus from '../EventBus';
import Logger from '../Logger';
import Settings from '../Settings';
@@ -413,7 +414,7 @@ export class Aard {
/**
* Checks whether autodetection can run
*/
- startCheck() {
+ startCheck(arVariant?: ArVariant) {
console.log('aard - starting checks')
if (!this.videoData.player) {
console.warn('Player not detected!');
diff --git a/src/ext/lib/video-transform/Resizer.ts b/src/ext/lib/video-transform/Resizer.ts
index b6a380e..031e6ce 100644
--- a/src/ext/lib/video-transform/Resizer.ts
+++ b/src/ext/lib/video-transform/Resizer.ts
@@ -16,7 +16,7 @@ import VideoData from '../video-data/VideoData';
import EventBus from '../EventBus';
import { _cp } from '../../../common/js/utils';
import Settings from '../Settings';
-import { Ar } from '../../../common/interfaces/ArInterface';
+import { Ar, ArVariant } from '../../../common/interfaces/ArInterface';
import { RunLevel } from '../../enum/run-level.enum';
import * as _ from 'lodash';
import getElementStyles from '../../util/getElementStyles';
@@ -26,6 +26,11 @@ if(Debug.debug) {
console.log("Loading: Resizer.js");
}
+enum ResizerMode {
+ Crop = 1,
+ Zoom = 2
+};
+
/**
* Resizer is the top class and is responsible for figuring out which component needs to crop, which
* component needs to zoom, and which component needs to stretch.
@@ -83,9 +88,9 @@ class Resizer {
//#endregion
cycleableAspectRatios: Ar[];
+ cycleableZoomAspectRatios: Ar[];
nextCycleOptionIndex = 0;
-
//#region event bus configuration
private eventBusCommands = {
'set-ar': [{
@@ -93,7 +98,7 @@ class Resizer {
this.manualZoom = false; // this only gets called from UI or keyboard shortcuts, making this action safe.
if (config.type !== AspectRatioType.Cycle) {
- this.setAr(config);
+ this.setAr({...config, variant: ArVariant.Crop});
} else {
// if we manually switched to a different aspect ratio, cycle from that ratio forward
const lastArIndex = this.cycleableAspectRatios.findIndex(x => x.type === this.lastAr.type && x.ratio === this.lastAr.ratio);
@@ -101,11 +106,29 @@ class Resizer {
this.nextCycleOptionIndex = (lastArIndex + 1) % this.cycleableAspectRatios.length;
}
- this.setAr(this.cycleableAspectRatios[this.nextCycleOptionIndex]);
+ this.setAr({...this.cycleableAspectRatios[this.nextCycleOptionIndex], variant: ArVariant.Crop});
this.nextCycleOptionIndex = (this.nextCycleOptionIndex + 1) % this.cycleableAspectRatios.length;
}
}
}],
+ 'set-ar-zoom': [{
+ function: (config: any) => {
+ this.manualZoom = false; // this only gets called from UI or keyboard shortcuts, making this action safe.
+
+ if (config.type !== AspectRatioType.Cycle) {
+ this.setAr({...config, variant: ArVariant.Zoom});
+ } else {
+ // if we manually switched to a different aspect ratio, cycle from that ratio forward
+ const lastArIndex = this.cycleableZoomAspectRatios.findIndex(x => x.type === this.lastAr.type && x.ratio === this.lastAr.ratio);
+ if (lastArIndex !== -1) {
+ this.nextCycleOptionIndex = (lastArIndex + 1) % this.cycleableZoomAspectRatios.length;
+ }
+
+ this.setAr({...this.cycleableZoomAspectRatios[this.nextCycleOptionIndex], variant: ArVariant.Zoom});
+ this.nextCycleOptionIndex = (this.nextCycleOptionIndex + 1) % this.cycleableZoomAspectRatios.length;
+ }
+ }
+ }],
'set-alignment': [{
function: (config: any) => {
this.setVideoAlignment(config.x, config.y);
@@ -188,6 +211,11 @@ class Resizer {
.filter(x => [AspectRatioType.FitHeight, AspectRatioType.FitWidth, AspectRatioType.Fixed, AspectRatioType.Reset].includes(x?.arguments?.type))
.map(x => x.arguments) as Ar[];
+ this.cycleableZoomAspectRatios =
+ (this.settings?.active?.commands?.zoom ?? [])
+ .filter(x => x.action === 'set-ar-zoom' && x.arguments?.type !== AspectRatioType.Cycle)
+ .map(x => x.arguments) as Ar[];
+
this.nextCycleOptionIndex = 0;
this.userCssClassName = videoData.userCssClassName;
}
@@ -276,8 +304,27 @@ class Resizer {
}
}
+ /**
+ * Starts and stops Aard as necessary. Returns 'true' if we can
+ * stop setting aspect ratio early.
+ * @param ar
+ * @param resizerMode
+ * @returns
+ */
+ private handleAard(ar: Ar): boolean {
+ if (ar.type === AspectRatioType.Automatic) {
+ this.videoData.aard?.startCheck(ar.variant);
+ return true;
+ } else if (ar.type !== AspectRatioType.AutomaticUpdate) {
+ this.videoData.aard?.stop();
+ } else if (this.stretcher.stretch.type === StretchType.Basic) {
+ this.videoData?.aard?.stop();
+ }
+
+ }
+
async setAr(ar: Ar, lastAr?: Ar) {
- if (this.destroyed) {
+ if (this.destroyed || ar == null) {
return;
}
@@ -294,11 +341,8 @@ class Resizer {
}
// handle autodetection stuff
- if (ar.type === AspectRatioType.Automatic) {
- this.videoData.aard?.startCheck();
+ if (this.handleAard(ar)) {
return;
- } else if (ar.type !== AspectRatioType.AutomaticUpdate) {
- this.videoData.aard?.stop();
}
if (ar.type !== AspectRatioType.AutomaticUpdate) {
@@ -312,10 +356,6 @@ class Resizer {
this.logger.log('info', 'debug', '%c[Resizer::setAr] trying to set ar. New ar:', 'background-color: #4c3a2f, color: #ffa349', ar);
- if (ar == null) {
- return;
- }
-
let stretchFactors: VideoDimensions | any;
// reset zoom, but only on aspect ratio switch. We also know that aspect ratio gets converted to
@@ -324,6 +364,7 @@ class Resizer {
(ar.type !== AspectRatioType.Fixed && ar.type !== AspectRatioType.Manual) // anything not these two _always_ changes AR
|| ar.type !== this.lastAr.type // this also means aspect ratio has changed
|| ar.ratio !== this.lastAr.ratio // this also means aspect ratio has changed
+ || ar.variant !== this.lastAr.variant
) {
this.zoom.reset();
this.resetPan();
@@ -360,26 +401,11 @@ class Resizer {
this.videoData.destroy();
}
- // pause AR on:
- // * ar.type NOT automatic
- // * ar.type is auto, but stretch is set to basic basic stretch
- //
- // unpause when using other modes
- if ((ar.type !== AspectRatioType.Automatic && ar.type !== AspectRatioType.AutomaticUpdate) || this.stretcher.stretch.type === StretchType.Basic) {
- this.videoData?.aard?.stop();
- } else {
- if (ar.type !== AspectRatioType.AutomaticUpdate) {
- if (this.lastAr.type === AspectRatioType.Automatic || this.lastAr.type === AspectRatioType.AutomaticUpdate) {
- this.videoData?.aard?.stop();
- }
- }
- }
-
// do stretch thingy
if ([StretchType.NoStretch, StretchType.Conditional, StretchType.FixedSource].includes(this.stretcher.stretch.type)) {
stretchFactors = this.scaler.calculateCrop(ar);
- if(! stretchFactors || stretchFactors.error){
+ if (!stretchFactors || stretchFactors.error){
this.logger.log('error', 'debug', `[Resizer::setAr] failed to set AR due to problem with calculating crop. Error:`, stretchFactors?.error);
if (stretchFactors?.error === 'no_video'){
this.videoData.destroy();
@@ -406,12 +432,11 @@ class Resizer {
this.stretcher.stretch.type === StretchType.Conditional ? 'crop with conditional StretchType.' : 'crop with fixed stretch',
'Stretch factors are:', stretchFactors
);
-
} else if (this.stretcher.stretch.type === StretchType.Hybrid) {
stretchFactors = this.stretcher.calculateStretch(ar.ratio);
this.logger.log('info', 'debug', '[Resizer::setAr] Processed stretch factors for hybrid stretch/crop. Stretch factors are:', stretchFactors);
} else if (this.stretcher.stretch.type === StretchType.Fixed) {
- stretchFactors = this.stretcher.calculateStretchFixed(ar.ratio)
+ stretchFactors = this.stretcher.calculateStretchFixed(ar.ratio);
} else if (this.stretcher.stretch.type === StretchType.Basic) {
stretchFactors = this.stretcher.calculateBasicStretch();
this.logger.log('info', 'debug', '[Resizer::setAr] Processed stretch factors for basic StretchType. Stretch factors are:', stretchFactors);
@@ -794,8 +819,10 @@ class Resizer {
// conditions are true at the same time, we need to go 'chiny reckon' and recheck our player
// element. Chances are our video is not getting aligned correctly
if (
- (this.videoData.video.offsetWidth > this.videoData.player.dimensions.width && this.videoData.video.offsetHeight > this.videoData.player.dimensions.height) ||
- (this.videoData.video.offsetWidth < this.videoData.player.dimensions.width && this.videoData.video.offsetHeight < this.videoData.player.dimensions.height)
+ (
+ (this.videoData.video.offsetWidth > this.videoData.player.dimensions.width && this.videoData.video.offsetHeight > this.videoData.player.dimensions.height) ||
+ (this.videoData.video.offsetWidth < this.videoData.player.dimensions.width && this.videoData.video.offsetHeight < this.videoData.player.dimensions.height)
+ ) && ar?.variant !== ArVariant.Zoom
) {
this.logger.log('warn', ['debugger', 'resizer'], `[Resizer::_res_computeOffsets] 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`,
diff --git a/src/ext/lib/video-transform/Scaler.ts b/src/ext/lib/video-transform/Scaler.ts
index ca48d2b..a5a8728 100644
--- a/src/ext/lib/video-transform/Scaler.ts
+++ b/src/ext/lib/video-transform/Scaler.ts
@@ -3,6 +3,7 @@ import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
import BrowserDetect from '../../conf/BrowserDetect';
import VideoData from '../video-data/VideoData';
import Logger from '../Logger';
+import { Ar, ArVariant } from '../../../common/interfaces/ArInterface';
export enum CropStrategy {
@@ -100,7 +101,7 @@ class Scaler {
return null;
}
- calculateCrop(ar: {type: AspectRatioType, ratio?: number}): VideoDimensions | {error: string, [x: string]: any} {
+ calculateCrop(ar: Ar): VideoDimensions | {error: string, [x: string]: any} {
/**
* STEP 1: NORMALIZE ASPECT RATIO
*
@@ -170,9 +171,15 @@ class Scaler {
this.logger.log('info', 'scaler', "[Scaler::calculateCrop] trying to set ar. args are: ar->",ar.ratio,"; this.conf.player.dimensions->",this.conf.player.dimensions.width, "×", this.conf.player.dimensions.height, "| obj:", this.conf.player.dimensions);
+ // If we encounter invalid players, we try to update its dimensions
+ // ONCE before throwing an error
if( (! this.conf.player.dimensions) || this.conf.player.dimensions.width === 0 || this.conf.player.dimensions.height === 0 ){
this.logger.log('error', 'scaler', "[Scaler::calculateCrop] ERROR — no (or invalid) this.conf.player.dimensions:",this.conf.player.dimensions);
- return {error: "this.conf.player.dimensions_error"};
+ this.conf.player.updatePlayer();
+
+ if( (! this.conf.player.dimensions) || this.conf.player.dimensions.width === 0 || this.conf.player.dimensions.height === 0 ){
+ return {error: "this.conf.player.dimensions_error"};
+ }
}
// we can finally start computing required video dimensions now:
@@ -184,7 +191,7 @@ class Scaler {
}
- this.logger.log('info', 'scaler', "[Scaler::calculateCrop] ar is " ,ar.ratio, ", file ar is", streamAr, ", this.conf.player.dimensions are ", this.conf.player.dimensions.width, "×", this.conf.player.dimensions.height, "| obj:", this.conf.player.dimensions);
+ this.logger.log('info', 'scaler', "[Scaler::calculateCrop] ar is " ,ar.ratio, ", file ar is", streamAr, ",ar variant", ar.variant ,"\nthis.conf.player.dimensions are ", this.conf.player.dimensions.width, "×", this.conf.player.dimensions.height, "| obj:", this.conf.player.dimensions, this.conf.player.element);
const videoDimensions: VideoDimensions = {
xFactor: 1,
@@ -199,7 +206,7 @@ class Scaler {
}
}
- this.calculateCropCore(videoDimensions, ar.ratio, streamAr, playerAr)
+ this.calculateCropCore(videoDimensions, ar.ratio, streamAr, playerAr, ar.variant)
return videoDimensions;
}
@@ -212,7 +219,11 @@ class Scaler {
* @param {*} streamAr
* @param {*} playerAr
*/
- calculateCropCore(videoDimensions: VideoDimensions, ar: number, streamAr: number, playerAr: number) {
+ calculateCropCore(videoDimensions: VideoDimensions, ar: number, streamAr: number, playerAr: number, variant?: ArVariant) {
+ if (variant === ArVariant.Zoom) {
+ playerAr = ar;
+ }
+
if (streamAr < playerAr) {
if (streamAr < ar){
// in this situation we have to crop letterbox on top/bottom of the player
@@ -254,8 +265,8 @@ class Scaler {
const letterboxRatio = (1 - (playerAr / ar));
videoDimensions.relativeCropLimits = {
- top: ar > streamAr ? ( ar > playerAr ? (letterboxRatio * -0.5) : 0) : 0,
- left: ar < streamAr ? ( ar < playerAr ? (-0.5 / letterboxRatio) : 0) : 0,
+ top: ar > streamAr ? ( ar >= playerAr ? (letterboxRatio * -0.5) : 0) : 0,
+ left: ar < streamAr ? ( ar <= playerAr ? (-0.5 / letterboxRatio) : 0) : 0,
}
videoDimensions.preventAlignment = {
x: ar > playerAr, // video is wider than player, so it's full width already