Compare commits

..

2 Commits

Author SHA1 Message Date
cd89cca984 fix vertical alignment 2024-12-30 03:05:47 +01:00
25222c7310 Prevent overlay from blocking the video player UI 2024-12-30 00:55:58 +01:00
3 changed files with 68 additions and 62 deletions

View File

@ -14,7 +14,7 @@
<div <div
v-if="contextMenuActive || settingsInitialized && uwTriggerZoneVisible && !isGlobal" v-if="contextMenuActive || settingsInitialized && uwTriggerZoneVisible && !isGlobal"
class="context-spawn uw-clickable uw-ui-area" class="context-spawn uw-ui-area"
style="z-index: 1001" style="z-index: 1001"
> >
@ -24,18 +24,17 @@
@mouseleave="allowContextMenuHide()" @mouseleave="allowContextMenuHide()"
> >
<template v-slot:activator> <template v-slot:activator>
<div class="context-item"> <div class="context-item uw-clickable">
Ultrawidify Ultrawidify
</div> </div>
</template> </template>
<slot> <slot>
<!-- <!--
Didn't manage to ensure that extension status pops up above other menu items in less than 3 minutes with z-index, Didn't manage to ensure that extension status pops up above other menu items in less than 3 minutes with z-index,
so wrapping 'status' and 'real menu items' in two different divs, ordering them in the opposite way, and then so wrapping 'status' and 'real menu items' in two different divs, ordering them in the opposite way, and then
ensuring correct ordering with flex-direction: column-reverse ended up being easier and faster. ensuring correct ordering with flex-direction: column-reverse ended up being easier and faster.
--> -->
<div class="menu-width flex-reverse-order"> <div class="uw-clickable menu-width flex-reverse-order">
<div style="z-index: 1000"> <div style="z-index: 1000">
<GhettoContextMenu alignment="right"> <GhettoContextMenu alignment="right">
<template v-slot:activator> <template v-slot:activator>

View File

@ -278,7 +278,7 @@ class Resizer {
} }
if ([AspectRatioType.Reset, AspectRatioType.Initial].includes(ar.type)) { if ([AspectRatioType.Reset, AspectRatioType.Initial].includes(ar.type)) {
console.log('run level is UI only because aspect ratio type is', ar.type) // console.log('run level is UI only because aspect ratio type is', ar.type)
this.eventBus.send('set-run-level', RunLevel.UIOnly); this.eventBus.send('set-run-level', RunLevel.UIOnly);
} else { } else {
this.eventBus.send('set-run-level', RunLevel.CustomCSSActive); this.eventBus.send('set-run-level', RunLevel.CustomCSSActive);
@ -307,7 +307,7 @@ class Resizer {
return; return;
} }
let stretchFactors: {xFactor: number, yFactor: number, arCorrectionFactor?: number, ratio?: number} | any; let stretchFactors: VideoDimensions | any;
// reset zoom, but only on aspect ratio switch. We also know that aspect ratio gets converted to // reset zoom, but only on aspect ratio switch. We also know that aspect ratio gets converted to
// AspectRatioType.Fixed when zooming, so let's keep that in mind // AspectRatioType.Fixed when zooming, so let's keep that in mind
@ -419,10 +419,10 @@ class Resizer {
); );
} }
this.applyScaling(stretchFactors); this.applyScaling(stretchFactors as VideoDimensions);
} }
applyScaling(stretchFactors, options?: {noAnnounce?: boolean}) { applyScaling(stretchFactors: VideoDimensions, options?: {noAnnounce?: boolean, ar?: Ar}) {
this.stretcher.chromeBugMitigation(stretchFactors); this.stretcher.chromeBugMitigation(stretchFactors);
// let the UI know // let the UI know
@ -430,7 +430,7 @@ class Resizer {
this.videoData.eventBus.send('announce-zoom', {x: stretchFactors.xFactor, y: stretchFactors.yFactor}); this.videoData.eventBus.send('announce-zoom', {x: stretchFactors.xFactor, y: stretchFactors.yFactor});
} }
let translate = this.computeOffsets(stretchFactors); let translate = this.computeOffsets(stretchFactors, options?.ar);
this.applyCss(stretchFactors, translate); this.applyCss(stretchFactors, translate);
} }
@ -457,7 +457,7 @@ class Resizer {
if(!this.videoData.player || !this.videoData.player.element) { if(!this.videoData.player || !this.videoData.player.element) {
return; return;
} }
// dont allow weird floats // don't allow weird floats
this.videoAlignment.x = VideoAlignmentType.Center; this.videoAlignment.x = VideoAlignmentType.Center;
// because non-fixed aspect ratios reset panning: // because non-fixed aspect ratios reset panning:
@ -501,14 +501,14 @@ class Resizer {
setVideoAlignment(videoAlignmentX: VideoAlignmentType, videoAlignmentY?: VideoAlignmentType) { setVideoAlignment(videoAlignmentX: VideoAlignmentType, videoAlignmentY?: VideoAlignmentType) {
// if aspect ratio is unset or initial, CSS fixes are inactive by design. // if aspect ratio is unset or initial, CSS fixes are inactive by design.
// because of that, we need to set a manual aspect ratio first. // because of that, we need to set a manual aspect ratio first.
console.log('last aspect ratio:', this.lastAr);
if (!this.lastAr) { if (!this.lastAr?.ratio) {
console.warn('[Resizer.js::setVideoAlignment] Aspect ratio not set. This is illegal. This function will do nothing.');
this.setAr({ this.setAr({
type: AspectRatioType.Fixed, type: AspectRatioType.AutomaticUpdate,
ratio: this.getFileAr() ratio: this.getFileAr()
}); });
} }
if ([AspectRatioType.Reset, AspectRatioType.Initial].includes(this.lastAr.type)) { if ([AspectRatioType.Reset, AspectRatioType.Initial].includes(this.lastAr.type)) {
if (this.lastAr.ratio) { if (this.lastAr.ratio) {
this.lastAr.type = AspectRatioType.Fixed; this.lastAr.type = AspectRatioType.Fixed;
@ -659,36 +659,6 @@ class Resizer {
} }
} }
computeCroppedAreas(stretchFactors) {
// PSA: offsetWidth and offsetHeight DO NOT INCLUDE
// ZOOM APPLIED THROUGH THE MAGIC OF CSS TRANSFORMS
const sourceWidth = this.videoData.video.offsetWidth;
const sourceHeight = this.videoData.video.offsetHeight;
// this is the size of the video AFTER zooming was applied but does
// not account for cropping. It may be bigger than the player in
// both dimensions. It may be smaller than player in both dimensions
const postZoomWidth = sourceWidth * stretchFactors.xFactor;
const postZoomHeight = sourceHeight * stretchFactors.yFactor;
// this is the size of the video after crop is applied
const displayedWidth = Math.min(this.videoData.player.dimensions.width, postZoomWidth);
const displayedHeight = Math.min(this.videoData.player.dimensions.height, postZoomHeight);
// these two are cropped areas. Negative values mean additional
// letterboxing or pillarboxing. We assume center alignment for
// the time being - we will correct that later if need be
const croppedX = (postZoomWidth - displayedWidth) * 0.5;
const croppedY = (postZoomHeight - displayedHeight) * 0.5;
return {
sourceVideoDimensions: {width: sourceWidth, height: sourceHeight},
postZoomVideoDimensions: {width: postZoomWidth, height: postZoomHeight},
displayedVideoDimensions: {width: displayedWidth, height: displayedHeight},
crop: {left: croppedX, top: croppedY},
};
}
/** /**
* Sometimes, sites (e.g. new reddit) will guarantee that video fits width of its container * Sometimes, sites (e.g. new reddit) will guarantee that video fits width of its container
* and let the browser figure out the height through the magic of height:auto. This is bad, * and let the browser figure out the height through the magic of height:auto. This is bad,
@ -716,14 +686,12 @@ class Resizer {
} }
private _computeOffsetsRecursionGuard: boolean = false; private _computeOffsetsRecursionGuard: boolean = false;
computeOffsets(stretchFactors: VideoDimensions){ computeOffsets(stretchFactors: VideoDimensions, ar?: Ar){
this.logger.log('info', 'debug', "[Resizer::computeOffsets] <rid:"+this.resizerId+"> video will be aligned to ", this.videoAlignment); this.logger.log('info', 'debug', "[Resizer::computeOffsets] <rid:"+this.resizerId+"> video will be aligned to ", this.videoAlignment);
const {realVideoWidth, realVideoHeight, marginX, marginY} = this.computeVideoDisplayedDimensions(); const {realVideoWidth, realVideoHeight, marginX, marginY} = this.computeVideoDisplayedDimensions();
const {postZoomVideoDimensions, displayedVideoDimensions, crop} = this.computeCroppedAreas(stretchFactors); // correct any remaining element size discrepancies (applicable only to certain crop strategies!)
// correct any remaining element size discrepencies (applicable only to certain crop strategies!)
// NOTE: it's possible that we might also need to apply a similar measure for CropPillarbox strategy // NOTE: it's possible that we might also need to apply a similar measure for CropPillarbox strategy
// (but we'll wait for bug reports before doing so). // (but we'll wait for bug reports before doing so).
// We also don't compensate for height:auto if height is provided via element style // We also don't compensate for height:auto if height is provided via element style
@ -732,7 +700,11 @@ class Resizer {
stretchFactors.cropStrategy === CropStrategy.CropLetterbox stretchFactors.cropStrategy === CropStrategy.CropLetterbox
&& (!stretchFactors.styleHeightCompensationFactor || stretchFactors.styleHeightCompensationFactor === 1) && (!stretchFactors.styleHeightCompensationFactor || stretchFactors.styleHeightCompensationFactor === 1)
) { ) {
autoHeightCompensationFactor = this.computeAutoHeightCompensationFactor(realVideoWidth, realVideoHeight, this.videoData.player.dimensions.width, this.videoData.player.dimensions.height, 'height'); autoHeightCompensationFactor = this.computeAutoHeightCompensationFactor(
realVideoWidth, realVideoHeight,
this.videoData.player.dimensions.width, this.videoData.player.dimensions.height,
'height'
);
stretchFactors.xFactor *= autoHeightCompensationFactor; stretchFactors.xFactor *= autoHeightCompensationFactor;
stretchFactors.yFactor *= autoHeightCompensationFactor; stretchFactors.yFactor *= autoHeightCompensationFactor;
} }
@ -759,17 +731,21 @@ class Resizer {
} }
} else { } else {
// correct horizontal alignment according to the settings // correct horizontal alignment according to the settings
if (!stretchFactors.preventAlignment?.x) {
if (this.videoAlignment.x == VideoAlignmentType.Left) { if (this.videoAlignment.x == VideoAlignmentType.Left) {
translate.x += alignXOffset; translate.x += stretchFactors?.relativeCropLimits?.left ? (this.videoData.player.dimensions.width * stretchFactors.relativeCropLimits.left): alignXOffset;
} else if (this.videoAlignment.x == VideoAlignmentType.Right) { } else if (this.videoAlignment.x == VideoAlignmentType.Right) {
translate.x -= alignXOffset translate.x -= stretchFactors?.relativeCropLimits?.left ? (this.videoData.player.dimensions.width * stretchFactors.relativeCropLimits.left): alignXOffset
}
} }
// correct vertical alignment according to the settings // correct vertical alignment according to the settings
if (!stretchFactors.preventAlignment?.y) {
if (this.videoAlignment.y == VideoAlignmentType.Top) { if (this.videoAlignment.y == VideoAlignmentType.Top) {
translate.y += alignYOffset; translate.y += stretchFactors?.relativeCropLimits?.top ? (this.videoData.player.dimensions.height * stretchFactors?.relativeCropLimits?.top): alignYOffset;
} else if (this.videoAlignment.y == VideoAlignmentType.Bottom) { } else if (this.videoAlignment.y == VideoAlignmentType.Bottom) {
translate.y -= alignYOffset; translate.y -= stretchFactors?.relativeCropLimits?.top ? (this.videoData.player.dimensions.height * stretchFactors?.relativeCropLimits?.top): alignYOffset;
}
} }
} }

View File

@ -29,6 +29,14 @@ export type VideoDimensions = {
styleHeightCompensationFactor?: number; styleHeightCompensationFactor?: number;
actualWidth?: number; actualWidth?: number;
actualHeight?: number; actualHeight?: number;
relativeCropLimits?: {
top: number;
left: number;
},
preventAlignment?: {
x: boolean,
y: boolean
}
} }
// does video size calculations for zooming/cropping // does video size calculations for zooming/cropping
@ -92,7 +100,7 @@ class Scaler {
return null; return null;
} }
calculateCrop(ar: {type: AspectRatioType, ratio?: number}) { calculateCrop(ar: {type: AspectRatioType, ratio?: number}): VideoDimensions | {error: string, [x: string]: any} {
/** /**
* STEP 1: NORMALIZE ASPECT RATIO * STEP 1: NORMALIZE ASPECT RATIO
* *
@ -142,7 +150,16 @@ class Scaler {
} }
if (ar.type === AspectRatioType.Reset){ if (ar.type === AspectRatioType.Reset){
return {xFactor: arCorrectionFactor, yFactor: arCorrectionFactor, arCorrectionFactor: arCorrectionFactor}; return {
xFactor: arCorrectionFactor,
yFactor: arCorrectionFactor,
arCorrectionFactor: arCorrectionFactor,
relativeCropLimits: {
top: 0,
left: 0,
}
};
} }
// handle fuckie-wuckies // handle fuckie-wuckies
@ -175,7 +192,11 @@ class Scaler {
actualWidth: 0, // width of the video (excluding pillarbox) when <video> tag height is equal to width actualWidth: 0, // width of the video (excluding pillarbox) when <video> tag height is equal to width
actualHeight: 0, // height of the video (excluding letterbox) when <video> tag height is equal to height actualHeight: 0, // height of the video (excluding letterbox) when <video> tag height is equal to height
arCorrectionFactor: arCorrectionFactor, arCorrectionFactor: arCorrectionFactor,
styleHeightCompensationFactor: heightCompensationFactor styleHeightCompensationFactor: heightCompensationFactor,
relativeCropLimits: {
top: 0,
left: 0
}
} }
this.calculateCropCore(videoDimensions, ar.ratio, streamAr, playerAr) this.calculateCropCore(videoDimensions, ar.ratio, streamAr, playerAr)
@ -223,14 +244,24 @@ class Scaler {
} }
} }
this.logger.log('info', 'scaler', "[Scaler::calculateCrop] Crop factor calculated — ", videoDimensions.xFactor);
// correct the scale factor // correct the scale factor
if (videoDimensions.arCorrectionFactor) { if (videoDimensions.arCorrectionFactor) {
videoDimensions.xFactor *= videoDimensions.arCorrectionFactor; videoDimensions.xFactor *= videoDimensions.arCorrectionFactor;
videoDimensions.yFactor *= videoDimensions.arCorrectionFactor; videoDimensions.yFactor *= videoDimensions.arCorrectionFactor;
} }
// Add crop limits — needed for vertical alignment in order to
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,
}
videoDimensions.preventAlignment = {
x: ar > playerAr, // video is wider than player, so it's full width already
y: ar < playerAr, // video is narrower than player, so it's full height already
}
return videoDimensions; return videoDimensions;
} }
} }