Add poor man's "Clarity Boost" feature (#94)

* Add Video clarity setting

* Disable Clarity feature in Safari

* Make the Video settings bar smaller

* Stop loading the page when running into the "We are sorry..." error message

* Fix problems with named parameters
This commit is contained in:
redphx 2023-08-13 16:22:13 +07:00 committed by GitHub
parent 1d0d69850f
commit dce0a44d2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -18,13 +18,29 @@ console.log(`[Better xCloud] readyState: ${document.readyState}`);
// Quickly create a tree of elements without having to use innerHTML // Quickly create a tree of elements without having to use innerHTML
function createElement(elmName, props = {}) { function createElement(elmName, props = {}) {
const $elm = document.createElement(elmName); let $elm;
const hasNs = 'xmlns' in props;
if (hasNs) {
$elm = document.createElementNS(props.xmlns, elmName);
} else {
$elm = document.createElement(elmName);
}
for (let key in props) { for (let key in props) {
if (key === 'xmlns') {
continue;
}
if (!props.hasOwnProperty(key) || $elm.hasOwnProperty(key)) { if (!props.hasOwnProperty(key) || $elm.hasOwnProperty(key)) {
continue; continue;
} }
$elm.setAttribute(key, props[key]); if (hasNs) {
$elm.setAttributeNS(null, key, props[key]);
} else {
$elm.setAttribute(key, props[key]);
}
} }
for (let i = 2, size = arguments.length; i < size; i++) { for (let i = 2, size = arguments.length; i < size; i++) {
@ -79,6 +95,7 @@ window.addEventListener('load', e => {
setTimeout(() => { setTimeout(() => {
if (document.body.classList.contains('legacyBackground')) { if (document.body.classList.contains('legacyBackground')) {
// Has error message -> reload page // Has error message -> reload page
window.stop();
window.location.reload(true); window.location.reload(true);
} }
}, 2000); }, 2000);
@ -765,8 +782,12 @@ class UserAgent {
[UserAgent.PROFILE_SMARTTV_TIZEN]: 'Mozilla/5.0 (SMART-TV; LINUX; Tizen 7.0) AppleWebKit/537.36 (KHTML, like Gecko) 94.0.4606.31/7.0 TV Safari/537.36', [UserAgent.PROFILE_SMARTTV_TIZEN]: 'Mozilla/5.0 (SMART-TV; LINUX; Tizen 7.0) AppleWebKit/537.36 (KHTML, like Gecko) 94.0.4606.31/7.0 TV Safari/537.36',
} }
static getDefault() {
return window.navigator.orgUserAgent || window.navigator.userAgent;
}
static get(profile) { static get(profile) {
const defaultUserAgent = window.navigator.orgUserAgent || window.navigator.userAgent; const defaultUserAgent = UserAgent.getDefault();
if (profile === UserAgent.PROFILE_CUSTOM) { if (profile === UserAgent.PROFILE_CUSTOM) {
return PREFS.get(Preferences.USER_AGENT_CUSTOM, ''); return PREFS.get(Preferences.USER_AGENT_CUSTOM, '');
} }
@ -774,6 +795,17 @@ class UserAgent {
return UserAgent.#USER_AGENTS[profile] || defaultUserAgent; return UserAgent.#USER_AGENTS[profile] || defaultUserAgent;
} }
static isSafari(mobile=false) {
const userAgent = (UserAgent.getDefault() || '').toLowerCase();
let result = userAgent.includes('safari') && !userAgent.includes('chrom');
if (result && mobile) {
result = userAgent.includes('mobile');
}
return result;
}
static spoof() { static spoof() {
const profile = PREFS.get(Preferences.USER_AGENT_PROFILE); const profile = PREFS.get(Preferences.USER_AGENT_PROFILE);
if (profile === UserAgent.PROFILE_DEFAULT) { if (profile === UserAgent.PROFILE_DEFAULT) {
@ -861,6 +893,7 @@ class Preferences {
static get HIDE_DOTS_ICON() { return 'hide_dots_icon'; } static get HIDE_DOTS_ICON() { return 'hide_dots_icon'; }
static get REDUCE_ANIMATIONS() { return 'reduce_animations'; } static get REDUCE_ANIMATIONS() { return 'reduce_animations'; }
static get VIDEO_CLARITY() { return 'video_clarity'; }
static get VIDEO_FILL_FULL_SCREEN() { return 'video_fill_full_screen'; } static get VIDEO_FILL_FULL_SCREEN() { return 'video_fill_full_screen'; }
static get VIDEO_BRIGHTNESS() { return 'video_brightness'; } static get VIDEO_BRIGHTNESS() { return 'video_brightness'; }
static get VIDEO_CONTRAST() { return 'video_contrast'; } static get VIDEO_CONTRAST() { return 'video_contrast'; }
@ -1005,6 +1038,11 @@ class Preferences {
[Preferences.USER_AGENT_CUSTOM]: { [Preferences.USER_AGENT_CUSTOM]: {
'default': '', 'default': '',
}, },
[Preferences.VIDEO_CLARITY]: {
'default': 0,
'min': 0,
'max': 3,
},
[Preferences.VIDEO_FILL_FULL_SCREEN]: { [Preferences.VIDEO_FILL_FULL_SCREEN]: {
'default': false, 'default': false,
}, },
@ -1242,6 +1280,10 @@ function addCss() {
display: none !important; display: none !important;
} }
.better-xcloud-hidden {
visibility: hidden !important;
}
.better-xcloud-settings-wrapper { .better-xcloud-settings-wrapper {
width: 450px; width: 450px;
margin: auto; margin: auto;
@ -1598,17 +1640,17 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
user-select: none; user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
position: fixed; position: fixed;
bottom: 20px; bottom: 0;
left: 50%; left: 50%;
transform: translate(-50%, 0); transform: translate(-50%, 0);
z-index: 9999; z-index: 9999;
padding: 20px; padding: 16px;
width: 620px; width: 600px;
background: #1a1b1e; background: #1a1b1e;
color: #fff; color: #fff;
border-radius: 8px; border-radius: 8px 8px 0 0;
font-weight: 400; font-weight: 400;
font-size: 16px; font-size: 14px;
font-family: Bahnschrift, Arial, Helvetica, sans-serif; font-family: Bahnschrift, Arial, Helvetica, sans-serif;
text-align: center; text-align: center;
box-shadow: 0px 0px 6px #000; box-shadow: 0px 0px 6px #000;
@ -1624,22 +1666,22 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
} }
.better-xcloud-quick-settings-bar label { .better-xcloud-quick-settings-bar label {
font-size: 20px; font-size: 16px;
display: block; display: block;
margin-bottom: 8px; margin-bottom: 8px;
} }
.better-xcloud-quick-settings-bar input { .better-xcloud-quick-settings-bar input {
width: 24px; width: 22px;
height: 24px; height: 22px;
} }
.better-xcloud-quick-settings-bar button { .better-xcloud-quick-settings-bar button {
border: none; border: none;
width: 24px; width: 22px;
height: 24px; height: 22px;
margin: 0 8px; margin: 0 4px;
line-height: 24px; line-height: 22px;
background-color: #515151; background-color: #515151;
color: #fff; color: #fff;
border-radius: 4px; border-radius: 4px;
@ -2319,6 +2361,15 @@ function injectSettingsButton($parent) {
function getVideoPlayerFilterStyle() { function getVideoPlayerFilterStyle() {
const filters = []; const filters = [];
const clarity = PREFS.get(Preferences.VIDEO_CLARITY);
if (clarity != 0) {
const level = 7 - (clarity - 1); // 5,6,7
const matrix = `0 -1 0 -1 ${level} -1 0 -1 0`;
document.getElementById('better-xcloud-filter-clarity-matrix').setAttributeNS(null, 'kernelMatrix', matrix);
filters.push(`url(#better-xcloud-filter-clarity)`);
}
const saturation = PREFS.get(Preferences.VIDEO_SATURATION); const saturation = PREFS.get(Preferences.VIDEO_SATURATION);
if (saturation != 100) { if (saturation != 100) {
filters.push(`saturate(${saturation}%)`); filters.push(`saturate(${saturation}%)`);
@ -2341,8 +2392,22 @@ function getVideoPlayerFilterStyle() {
function updateVideoPlayerCss() { function updateVideoPlayerCss() {
let $elm = document.getElementById('better-xcloud-video-css'); let $elm = document.getElementById('better-xcloud-video-css');
if (!$elm) { if (!$elm) {
$elm = createElement('style', {id: 'better-xcloud-video-css'}); const CE = createElement;
$elm = CE('style', {id: 'better-xcloud-video-css'});
document.documentElement.appendChild($elm); document.documentElement.appendChild($elm);
// Setup SVG filters
const $svg = CE('svg', {
'id': 'better-xcloud-video-filters',
'xmlns': 'http://www.w3.org/2000/svg',
'class': 'better-xcloud-gone',
}, CE('defs', {'xmlns': 'http://www.w3.org/2000/svg'},
CE('filter', {'id': 'better-xcloud-filter-clarity', 'xmlns': 'http://www.w3.org/2000/svg'},
CE('feConvolveMatrix', {'id': 'better-xcloud-filter-clarity-matrix', 'order': '3', 'xmlns': 'http://www.w3.org/2000/svg'}))
)
);
document.documentElement.appendChild($svg);
} }
let filters = getVideoPlayerFilterStyle(); let filters = getVideoPlayerFilterStyle();
@ -2632,20 +2697,31 @@ function patchRtcCodecs() {
} }
function numberPicker(key) { function numberPicker(key, suffix='', disabled=false) {
const setting = Preferences.SETTINGS[key]
let value = PREFS.get(key); let value = PREFS.get(key);
let $text, $decBtn, $incBtn; let $text, $decBtn, $incBtn;
const MIN = 0; const MIN = setting.min;
const MAX= 150; const MAX= setting.max;
const CE = createElement; const CE = createElement;
const $wrapper = CE('div', {}, const $wrapper = CE('div', {},
$decBtn = CE('button', {'data-type': 'dec'}, '-'), $decBtn = CE('button', {'data-type': 'dec'}, '-'),
$text = CE('span', {}, value + '%'), $text = CE('span', {}, value + suffix),
$incBtn = CE('button', {'data-type': 'inc'}, '+'), $incBtn = CE('button', {'data-type': 'inc'}, '+'),
); );
if (disabled) {
$incBtn.disabled = true;
$incBtn.classList.add('better-xcloud-hidden');
$decBtn.disabled = true;
$decBtn.classList.add('better-xcloud-hidden');
return $wrapper;
}
let interval; let interval;
let isHolding = false; let isHolding = false;
@ -2664,7 +2740,7 @@ function numberPicker(key) {
value = (value >= MAX) ? MAX : value + 1; value = (value >= MAX) ? MAX : value + 1;
} }
$text.textContent = value + '%'; $text.textContent = value + suffix;
PREFS.set(key, value); PREFS.set(key, value);
updateVideoPlayerCss(); updateVideoPlayerCss();
@ -2705,21 +2781,25 @@ function numberPicker(key) {
function setupVideoSettingsBar() { function setupVideoSettingsBar() {
const CE = createElement; const CE = createElement;
const isSafari = UserAgent.isSafari();
let $stretchInp; let $stretchInp;
const $wrapper = CE('div', {'class': 'better-xcloud-quick-settings-bar'}, const $wrapper = CE('div', {'class': 'better-xcloud-quick-settings-bar'},
CE('div', {}, CE('div', {},
CE('label', {'for': 'better-xcloud-quick-setting-stretch'}, 'Stretch Video'), CE('label', {'for': 'better-xcloud-quick-setting-stretch'}, 'Stretch Video'),
$stretchInp = CE('input', {'id': 'better-xcloud-quick-setting-stretch', 'type': 'checkbox'})), $stretchInp = CE('input', {'id': 'better-xcloud-quick-setting-stretch', 'type': 'checkbox'})),
CE('div', {},
CE('label', {}, 'Clarity'),
numberPicker(Preferences.VIDEO_CLARITY, '', isSafari)), // disable this feature in Safari
CE('div', {}, CE('div', {},
CE('label', {}, 'Saturation'), CE('label', {}, 'Saturation'),
numberPicker(Preferences.VIDEO_SATURATION)), numberPicker(Preferences.VIDEO_SATURATION, '%')),
CE('div', {}, CE('div', {},
CE('label', {}, 'Contrast'), CE('label', {}, 'Contrast'),
numberPicker(Preferences.VIDEO_CONTRAST)), numberPicker(Preferences.VIDEO_CONTRAST, '%')),
CE('div', {}, CE('div', {},
CE('label', {}, 'Brightness'), CE('label', {}, 'Brightness'),
numberPicker(Preferences.VIDEO_BRIGHTNESS)) numberPicker(Preferences.VIDEO_BRIGHTNESS, '%'))
); );
$stretchInp.checked = PREFS.get(Preferences.VIDEO_FILL_FULL_SCREEN); $stretchInp.checked = PREFS.get(Preferences.VIDEO_FILL_FULL_SCREEN);
@ -2943,7 +3023,7 @@ function disablePwa() {
} }
// Check if it's Safari on mobile // Check if it's Safari on mobile
if (userAgent.includes('mobile') && userAgent.includes('safari') && !userAgent.includes('chrom')) { if (UserAgent.isSafari(true)) {
// Disable the PWA prompt // Disable the PWA prompt
Object.defineProperty(window.navigator, 'standalone', { Object.defineProperty(window.navigator, 'standalone', {
value: true, value: true,