Fix the issue where videos on players that were narrower than the video got cropped a lil bit too much
This commit is contained in:
parent
6e1fe930ac
commit
769ff6aace
@ -277,6 +277,54 @@ class VideoData {
|
|||||||
return a < b + diff && a > b - diff
|
return a < b + diff && a > b - diff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the contents of the style attribute of the video element
|
||||||
|
* in a form of an object.
|
||||||
|
*/
|
||||||
|
getVideoStyle() {
|
||||||
|
// This will _always_ give us an array. Empty string gives an array
|
||||||
|
// that contains one element. That element is an empty string.
|
||||||
|
const styleArray = (this.video.getAttribute('style') || '').split(';');
|
||||||
|
|
||||||
|
const styleObject = {};
|
||||||
|
|
||||||
|
for (const style of styleArray) {
|
||||||
|
// not a valid CSS, so we skip those
|
||||||
|
if (style.indexOf(':') === -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// let's play _very_ safe
|
||||||
|
let {property, value} = style.split('!important')[0].split(':');
|
||||||
|
value = value.trim();
|
||||||
|
styleObject[property] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return styleObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some sites try to accomodate ultrawide users by "cropping" videos
|
||||||
|
* by setting 'style' attribute of the video element to 'height: X%',
|
||||||
|
* where 'X' is something greater than 100.
|
||||||
|
*
|
||||||
|
* This function gets that percentage and converts it into a factor.
|
||||||
|
*/
|
||||||
|
getHeightCompensationFactor() {
|
||||||
|
const heightStyle = this.getVideoStyle()?.height;
|
||||||
|
|
||||||
|
if (!heightStyle || !heightStyle.endsWith('%')) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const heightCompensationFactor = heightStyle.split('%')[0] / 100;
|
||||||
|
if (isNaN(heightCompensationFactor)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return heightCompensationFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
firstTimeArdInit(){
|
firstTimeArdInit(){
|
||||||
if(this.destroyed || this.invalid) {
|
if(this.destroyed || this.invalid) {
|
||||||
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
|
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
|
||||||
|
@ -177,43 +177,41 @@ class Resizer {
|
|||||||
this.conf.pageInfo.updateCurrentCrop(ar);
|
this.conf.pageInfo.updateCurrentCrop(ar);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ar.type === AspectRatio.Automatic ||
|
// if (ar.type === AspectRatio.Automatic ||
|
||||||
ar.type === AspectRatio.Reset && this.lastAr.type === AspectRatio.Initial) {
|
// ar.type === AspectRatio.Reset && this.lastAr.type === AspectRatio.Initial) {
|
||||||
// some sites do things that interfere with our site (and aspect ratio setting in general)
|
// // some sites do things that interfere with our site (and aspect ratio setting in general)
|
||||||
// first, we check whether video contains anything we don't like
|
// // first, we check whether video contains anything we don't like
|
||||||
if (siteSettings?.autoarPreventConditions?.videoStyleString) {
|
// if (siteSettings?.autoarPreventConditions?.videoStyleString) {
|
||||||
const styleString = (this.video.getAttribute('style') || '').split(';');
|
// const styleString = (this.video.getAttribute('style') || '').split(';');
|
||||||
|
|
||||||
if (siteSettings.autoarPreventConditions.videoStyleString.containsProperty) {
|
// if (siteSettings.autoarPreventConditions.videoStyleString.containsProperty) {
|
||||||
const bannedProperties = siteSettings.autoarPreventConditions.videoStyleString.containsProperty;
|
// const bannedProperties = siteSettings.autoarPreventConditions.videoStyleString.containsProperty;
|
||||||
for (const prop in bannedProperties) {
|
// for (const prop in bannedProperties) {
|
||||||
for (const s of styleString) {
|
// for (const s of styleString) {
|
||||||
if (s.trim().startsWith(prop)) {
|
// if (s.trim().startsWith(prop)) {
|
||||||
|
|
||||||
// check if css property has a list of allowed values:
|
// // check if css property has a list of allowed values:
|
||||||
if (bannedProperties[prop].allowedValues) {
|
// if (bannedProperties[prop].allowedValues) {
|
||||||
const styleValue = s.split(':')[1].trim();
|
// const styleValue = s.split(':')[1].trim();
|
||||||
|
|
||||||
// check if property value is on the list of allowed values
|
// // check if property value is on the list of allowed values
|
||||||
// if it's not, we aren't allowed to start aard
|
// // if it's not, we aren't allowed to start aard
|
||||||
if (bannedProperties[prop].allowedValues.indexOf(styleValue) === -1) {
|
// if (bannedProperties[prop].allowedValues.indexOf(styleValue) === -1) {
|
||||||
this.logger.log('error', 'debug', "%c[Resizer::setAr] video style contains forbidden css property/value combo: ", "color: #900, background: #100", prop, " — we aren't allowed to start autoar.")
|
// this.logger.log('error', 'debug', "%c[Resizer::setAr] video style contains forbidden css property/value combo: ", "color: #900, background: #100", prop, " — we aren't allowed to start autoar.")
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
// no allowed values, no problem. We have forbidden property
|
// // no allowed values, no problem. We have forbidden property
|
||||||
// and this means aard can't start.
|
// // and this means aard can't start.
|
||||||
this.logger.log('info', 'debug', "%c[Resizer::setAr] video style contains forbidden css property: ", "color: #900, background: #100", prop, " — we aren't allowed to start autoar.")
|
// this.logger.log('info', 'debug', "%c[Resizer::setAr] video style contains forbidden css property: ", "color: #900, background: #100", prop, " — we aren't allowed to start autoar.")
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (lastAr) {
|
if (lastAr) {
|
||||||
this.lastAr = this.calculateRatioForLegacyOptions(lastAr);
|
this.lastAr = this.calculateRatioForLegacyOptions(lastAr);
|
||||||
@ -229,11 +227,11 @@ class Resizer {
|
|||||||
this.lastAr = {type: ar.type, ratio: ar.ratio}
|
this.lastAr = {type: ar.type, ratio: ar.ratio}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.extensionMode === ExtensionMode.Basic && !PlayerData.isFullScreen() && ar.type !== AspectRatio.Reset) {
|
// if (this.extensionMode === ExtensionMode.Basic && !PlayerData.isFullScreen() && ar.type !== AspectRatio.Reset) {
|
||||||
// don't actually apply or calculate css when using basic mode if not in fullscreen
|
// // don't actually apply or calculate css when using basic mode if not in fullscreen
|
||||||
// ... unless we're resetting the aspect ratio to original
|
// // ... unless we're resetting the aspect ratio to original
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (! this.video) {
|
if (! this.video) {
|
||||||
this.conf.destroy();
|
this.conf.destroy();
|
||||||
@ -259,7 +257,6 @@ class Resizer {
|
|||||||
|
|
||||||
var stretchFactors = this.scaler.calculateCrop(ar);
|
var stretchFactors = this.scaler.calculateCrop(ar);
|
||||||
|
|
||||||
|
|
||||||
if(! stretchFactors || stretchFactors.error){
|
if(! stretchFactors || stretchFactors.error){
|
||||||
this.logger.log('error', 'debug', `[Resizer::setAr] <rid:${this.resizerId}> failed to set AR due to problem with calculating crop. Error:`, stretchFactors?.error);
|
this.logger.log('error', 'debug', `[Resizer::setAr] <rid:${this.resizerId}> failed to set AR due to problem with calculating crop. Error:`, stretchFactors?.error);
|
||||||
if (stretchFactors?.error === 'no_video'){
|
if (stretchFactors?.error === 'no_video'){
|
||||||
@ -297,7 +294,7 @@ class Resizer {
|
|||||||
var stretchFactors = this.stretcher.calculateBasicStretch();
|
var stretchFactors = this.stretcher.calculateBasicStretch();
|
||||||
this.logger.log('info', 'debug', '[Resizer::setAr] Processed stretch factors for basic stretch. Stretch factors are:', stretchFactors);
|
this.logger.log('info', 'debug', '[Resizer::setAr] Processed stretch factors for basic stretch. Stretch factors are:', stretchFactors);
|
||||||
} else {
|
} else {
|
||||||
var stretchFactors = {xFactor: 1, yFactor: 1}
|
var stretchFactors = {xFactor: 1, yFactor: 1};
|
||||||
this.logger.log('error', 'debug', '[Resizer::setAr] Okay wtf happened? If you see this, something has gone wrong', stretchFactors,"\n------[ i n f o d u m p ]------\nstretcher:", this.stretcher);
|
this.logger.log('error', 'debug', '[Resizer::setAr] Okay wtf happened? If you see this, something has gone wrong', stretchFactors,"\n------[ i n f o d u m p ]------\nstretcher:", this.stretcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +66,28 @@ class Scaler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
calculateCrop(ar) {
|
calculateCrop(ar) {
|
||||||
|
/**
|
||||||
|
* STEP 1: NORMALIZE ASPECT RATIO
|
||||||
|
*
|
||||||
|
* Video width is normalized based on 100% of the parent. That means if the player AR
|
||||||
|
* is narrower than video ar, we need to pre-downscale the video. This scaling already
|
||||||
|
* undoes any zoom that style="height:123%" on the video element adds.
|
||||||
|
*
|
||||||
|
* Quick notes:
|
||||||
|
* * when I say 'video AR', I actually mean aspect ratio after we've compensated for
|
||||||
|
* any possible 'height:' stuffs in the style attribute of the video tag
|
||||||
|
* * because video width is normalized on 100% of the parent, we don't need to correct
|
||||||
|
* anything when the player is wider than the video.
|
||||||
|
*/
|
||||||
|
const streamAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
|
||||||
|
const playerAr = this.conf.player.dimensions.width / this.conf.player.dimensions.height;
|
||||||
|
const compensatedStreamAr = streamAr * this.conf.videoData.getHeightCompensationFactor();
|
||||||
|
|
||||||
|
let arCorrectionFactor = 1;
|
||||||
|
if (playerAr < compensatedStreamAr) {
|
||||||
|
arCorrectionFactor = this.conf.player.dimensions.width / this.conf.video.offsetWidth;
|
||||||
|
}
|
||||||
|
|
||||||
if(!this.conf.video){
|
if(!this.conf.video){
|
||||||
this.logger.log('info', 'debug', "[Scaler::calculateCrop] ERROR — no video detected. Conf:", this.conf, "video:", this.conf.video, "video dimensions:", this.conf.video && this.conf.video.videoWidth, '×', this.conf.video && this.conf.video.videoHeight);
|
this.logger.log('info', 'debug', "[Scaler::calculateCrop] ERROR — no video detected. Conf:", this.conf, "video:", this.conf.video, "video dimensions:", this.conf.video && this.conf.video.videoWidth, '×', this.conf.video && this.conf.video.videoHeight);
|
||||||
|
|
||||||
@ -81,7 +103,7 @@ class Scaler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ar.type === AspectRatio.Reset){
|
if (ar.type === AspectRatio.Reset){
|
||||||
return {xFactor: 1, yFactor: 1}
|
return {xFactor: arCorrectionFactor, yFactor: arCorrectionFactor}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle fuckie-wuckies
|
// handle fuckie-wuckies
|
||||||
@ -102,15 +124,13 @@ class Scaler {
|
|||||||
|
|
||||||
// Dejansko razmerje stranic datoteke/<video> značke
|
// Dejansko razmerje stranic datoteke/<video> značke
|
||||||
// Actual aspect ratio of the file/<video> tag
|
// Actual aspect ratio of the file/<video> tag
|
||||||
var fileAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
|
|
||||||
var playerAr = this.conf.player.dimensions.width / this.conf.player.dimensions.height;
|
|
||||||
|
|
||||||
if (ar.type === AspectRatio.Initial || !ar.ratio) {
|
if (ar.type === AspectRatio.Initial || !ar.ratio) {
|
||||||
ar.ratio = fileAr;
|
ar.ratio = streamAr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.logger.log('info', 'scaler', "[Scaler::calculateCrop] ar is " ,ar.ratio, ", file ar is", fileAr, ", 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, ", this.conf.player.dimensions are ", this.conf.player.dimensions.width, "×", this.conf.player.dimensions.height, "| obj:", this.conf.player.dimensions);
|
||||||
|
|
||||||
var videoDimensions = {
|
var videoDimensions = {
|
||||||
xFactor: 1,
|
xFactor: 1,
|
||||||
@ -119,11 +139,11 @@ class Scaler {
|
|||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileAr < playerAr) {
|
if (streamAr < playerAr) {
|
||||||
if (fileAr < ar.ratio){
|
if (streamAr < ar.ratio){
|
||||||
// in this situation we have to crop letterbox on top/bottom of the player
|
// in this situation we have to crop letterbox on top/bottom of the player
|
||||||
// we cut it, but never more than the player
|
// we cut it, but never more than the player
|
||||||
videoDimensions.xFactor = Math.min(ar.ratio, playerAr) / fileAr;
|
videoDimensions.xFactor = Math.min(ar.ratio, playerAr) / streamAr;
|
||||||
videoDimensions.yFactor = videoDimensions.xFactor;
|
videoDimensions.yFactor = videoDimensions.xFactor;
|
||||||
} else {
|
} else {
|
||||||
// in this situation, we would be cutting pillarbox. Inside horizontal player.
|
// in this situation, we would be cutting pillarbox. Inside horizontal player.
|
||||||
@ -132,14 +152,14 @@ class Scaler {
|
|||||||
videoDimensions.yFactor = 1;
|
videoDimensions.yFactor = 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (fileAr < ar.ratio || playerAr < ar.ratio){
|
if (streamAr < ar.ratio || playerAr < ar.ratio){
|
||||||
// in this situation, we need to add extra letterbox on top of our letterbox
|
// in this situation, we need to add extra letterbox on top of our letterbox
|
||||||
// this means we simply don't crop anything _at all_
|
// this means we simply don't crop anything _at all_
|
||||||
videoDimensions.xFactor = 1;
|
videoDimensions.xFactor = 1;
|
||||||
videoDimensions.yFactor = 1;
|
videoDimensions.yFactor = 1;
|
||||||
} else {
|
} else {
|
||||||
// meant for handling pillarbox crop. not quite implemented.
|
// meant for handling pillarbox crop. not quite implemented.
|
||||||
videoDimensions.xFactor = fileAr / Math.min(ar.ratio, playerAr);
|
videoDimensions.xFactor = streamAr / Math.min(ar.ratio, playerAr);
|
||||||
videoDimensions.yFactor = videoDimensions.xFactor;
|
videoDimensions.yFactor = videoDimensions.xFactor;
|
||||||
// videoDimensions.xFactor = Math.max(ar.ratio, playerAr) * fileAr;
|
// videoDimensions.xFactor = Math.max(ar.ratio, playerAr) * fileAr;
|
||||||
// videoDimensions.yFactor = videoDimensions.xFactor;
|
// videoDimensions.yFactor = videoDimensions.xFactor;
|
||||||
@ -148,6 +168,10 @@ class Scaler {
|
|||||||
|
|
||||||
this.logger.log('info', 'scaler', "[Scaler::calculateCrop] Crop factor calculated — ", videoDimensions.xFactor);
|
this.logger.log('info', 'scaler', "[Scaler::calculateCrop] Crop factor calculated — ", videoDimensions.xFactor);
|
||||||
|
|
||||||
|
// correct the factors
|
||||||
|
videoDimensions.xFactor *= arCorrectionFactor;
|
||||||
|
videoDimensions.yFactor *= arCorrectionFactor;
|
||||||
|
|
||||||
return videoDimensions;
|
return videoDimensions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,6 +133,17 @@ squeezeFactor: ${squeezeFactor}`, '\nvideo', this.conf.video);
|
|||||||
return this.calculateStretch(actualAr, this.fixedStretchRatio);
|
return this.calculateStretch(actualAr, this.fixedStretchRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getArCorrectionFactor() {
|
||||||
|
const streamAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
|
||||||
|
const playerAr = this.conf.player.dimensions.width / this.conf.player.dimensions.height;
|
||||||
|
|
||||||
|
let arCorrectionFactor = 1;
|
||||||
|
if (playerAr < streamAr) {
|
||||||
|
arCorrectionFactor = this.conf.player.dimensions.width / this.conf.video.offsetWidth;
|
||||||
|
}
|
||||||
|
return arCorrectionFactor;
|
||||||
|
}
|
||||||
|
|
||||||
calculateStretch(actualAr, playerArOverride) {
|
calculateStretch(actualAr, playerArOverride) {
|
||||||
const playerAr = playerArOverride || this.conf.player.dimensions.width / this.conf.player.dimensions.height;
|
const playerAr = playerArOverride || this.conf.player.dimensions.width / this.conf.player.dimensions.height;
|
||||||
const videoAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
|
const videoAr = this.conf.video.videoWidth / this.conf.video.videoHeight;
|
||||||
@ -211,6 +222,11 @@ squeezeFactor: ${squeezeFactor}`, '\nvideo', this.conf.video);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// correct factors
|
||||||
|
const arCorrectionFactor = this.getArCorrectionFactor();
|
||||||
|
stretchFactors.xFactor *= arCorrectionFactor;
|
||||||
|
stretchFactors.yFactor *= arCorrectionFactor;
|
||||||
|
|
||||||
return stretchFactors;
|
return stretchFactors;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user