From 23c8e5bf70ab16ec1a4528c3df238c093d8eec9a Mon Sep 17 00:00:00 2001 From: fire332 <96039230+fire332@users.noreply.github.com> Date: Wed, 18 Dec 2024 03:30:55 -0800 Subject: [PATCH 1/3] fix shorts thumbnails --- src/ui.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ui.css b/src/ui.css index 99a8c569..3ba24649 100644 --- a/src/ui.css +++ b/src/ui.css @@ -75,3 +75,8 @@ position: absolute !important; width: 100% !important; } + +/* Fixes shorts thumbnails */ +.ytlr-tile-header-renderer--shorts { + z-index: -1; +} From 6722fb4b614dbb475ccf6ce8339f019fdcc1cc7f Mon Sep 17 00:00:00 2001 From: fire332 <96039230+fire332@users.noreply.github.com> Date: Wed, 18 Dec 2024 03:34:10 -0800 Subject: [PATCH 2/3] fix stretched/offset shorts video --- src/screensaver-fix.ts | 87 ++++++++++++++++++++++++++++++++++++++++++ src/userScript.js | 38 +----------------- 2 files changed, 89 insertions(+), 36 deletions(-) create mode 100644 src/screensaver-fix.ts diff --git a/src/screensaver-fix.ts b/src/screensaver-fix.ts new file mode 100644 index 00000000..7d108dce --- /dev/null +++ b/src/screensaver-fix.ts @@ -0,0 +1,87 @@ +/** + * On webOS, when a video element doesn't perfectly fill + * the entire screen, the screensaver can be kick in. + */ + +import { waitForChildAdd } from './utils'; + +/** + * document.querySelector but waits for the Element to be added if it doesn't already exist. + */ +async function requireElement( + cssSelectors: string, + expected: E +): Promise> { + const alreadyPresent = document.querySelector(cssSelectors); + if (alreadyPresent) { + if (!(alreadyPresent instanceof expected)) throw new Error(); + + // Cast required due to narrowing limitations. + // https://github.com/microsoft/TypeScript/issues/55241 + return alreadyPresent as InstanceType; + } + + const result = await waitForChildAdd( + document.body, + (node): node is Element => + node instanceof Element && node.matches(cssSelectors), + true + ); + + if (!(result instanceof expected)) throw new Error(); + return result as InstanceType; +} + +function isPlayerHidden(video: HTMLVideoElement) { + // Youtube uses display none sometimes along with a negative top to hide the HTMLVideoElement. + return video.style.display == 'none' || video.style.top.startsWith('-'); +} + +function isWatchPage() { + return document.body.classList.contains('WEB_PAGE_TYPE_WATCH'); +} + +const playerCtrlObs = new MutationObserver((mutations, obs) => { + // Only watch page has a full-screen player. + if (!isWatchPage()) { + obs.disconnect(); + return; + } + + const video = mutations[0]?.target; + if (!(video instanceof HTMLVideoElement)) throw new Error(); + const style = video.style; + + // Not sure if there will be a race condition so just in case. + if (isPlayerHidden(video)) return; + + const targetWidth = `${window.innerWidth}px`; + const targetHeight = `${window.innerHeight}px`; + const targetLeft = '0px'; + const targetTop = '0px'; + + /** + * Check to see if identical before assignment as some webOS versions will trigger a mutation + * event even if the assignment effectively does nothing, leading to an infinite loop. + */ + style.width !== targetWidth && (style.width = targetWidth); + style.height !== targetHeight && (style.height = targetHeight); + style.left !== targetLeft && (style.left = targetLeft); + style.top !== targetTop && (style.top = targetTop); +}); + +const bodyAttrObs = new MutationObserver(async () => { + if (!isWatchPage()) return; + + // Youtube TV re-uses the same video element for everything. + const video = await requireElement('video', HTMLVideoElement); + playerCtrlObs.observe(video, { + attributes: true, + attributeFilter: ['style'] + }); +}); + +bodyAttrObs.observe(document.body, { + attributes: true, + attributeFilter: ['class'] +}); diff --git a/src/userScript.js b/src/userScript.js index ec229230..5cbe069e 100644 --- a/src/userScript.js +++ b/src/userScript.js @@ -1,7 +1,7 @@ import 'whatwg-fetch'; import './domrect-polyfill'; -import { handleLaunch, waitForChildAdd } from './utils'; +import { handleLaunch } from './utils'; document.addEventListener( 'webOSRelaunch', @@ -18,38 +18,4 @@ import './sponsorblock.js'; import './ui.js'; import './font-fix.css'; import './thumbnail-quality'; - -// This IIFE is to keep the video element fill the entire window so that screensaver doesn't kick in. -(async () => { - /** @type {HTMLVideoElement} */ - const video = await waitForChildAdd( - document.body, - (node) => node instanceof HTMLVideoElement, - false - ); - - const playerCtrlObs = new MutationObserver(() => { - const style = video.style; - - const targetWidth = `${window.innerWidth}px`; - const targetHeight = `${window.innerHeight}px`; - const targetLeft = '0px'; - // YT uses a negative top to hide player when not in use. Don't know why but let's not affect it. - const targetTop = - style.top === `-${window.innerHeight}px` ? style.top : '0px'; - - /** - * Check to see if identical before assignment as some webOS versions will trigger a mutation - * mutation event even if the assignment effectively does nothing, leading to an infinite loop. - */ - style.width !== targetWidth && (style.width = targetWidth); - style.height !== targetHeight && (style.height = targetHeight); - style.left !== targetLeft && (style.left = targetLeft); - style.top !== targetTop && (style.top = targetTop); - }); - - playerCtrlObs.observe(video, { - attributes: true, - attributeFilter: ['style'] - }); -})(); +import './screensaver-fix'; From da8b3e14ba30f8e5e6864f7b4223e754f68507a3 Mon Sep 17 00:00:00 2001 From: fire332 <96039230+fire332@users.noreply.github.com> Date: Wed, 18 Dec 2024 03:34:49 -0800 Subject: [PATCH 3/3] default remove shorts config to false --- src/config.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/config.js b/src/config.js index 137c8f2d..bf2313f1 100644 --- a/src/config.js +++ b/src/config.js @@ -3,7 +3,10 @@ const CONFIG_KEY = 'ytaf-configuration'; const configOptions = new Map([ ['enableAdBlock', { default: true, desc: 'Enable ad blocking' }], ['upgradeThumbnails', { default: false, desc: 'Upgrade thumbnail quality' }], - ['removeShorts', { default: true, desc: 'Remove Shorts from subscriptions' }], + [ + 'removeShorts', + { default: false, desc: 'Remove Shorts from subscriptions' } + ], ['enableSponsorBlock', { default: true, desc: 'Enable SponsorBlock' }], [ 'enableSponsorBlockSponsor',