diff --git a/src/config.js b/src/config.js index 137c8f2..bf2313f 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', diff --git a/src/screensaver-fix.ts b/src/screensaver-fix.ts new file mode 100644 index 0000000..7d108dc --- /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/ui.css b/src/ui.css index 99a8c56..5d749cb 100644 --- a/src/ui.css +++ b/src/ui.css @@ -60,18 +60,3 @@ padding: 0 1em; line-height: 0; } - -/* Fixes transparency effect for the video player */ -.ytlr-watch-default__shadow { - background-image: linear-gradient( - to bottom, - rgba(0, 0, 0, 0) 0, - rgba(0, 0, 0, 0.8) 90% - ) !important; - background-color: rgba(0, 0, 0, 0.3) !important; - display: block !important; - height: 100% !important; - pointer-events: none !important; - position: absolute !important; - width: 100% !important; -} diff --git a/src/userScript.js b/src/userScript.js index ec22923..b035e04 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,5 @@ 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'; +import './yt-fixes.css'; diff --git a/src/yt-fixes.css b/src/yt-fixes.css new file mode 100644 index 0000000..3136426 --- /dev/null +++ b/src/yt-fixes.css @@ -0,0 +1,31 @@ +/* Fixes transparency effect for the video player */ +.ytlr-watch-default__shadow { + background-image: linear-gradient( + to bottom, + rgba(0, 0, 0, 0) 0, + rgba(0, 0, 0, 0.8) 90% + ) !important; + background-color: rgba(0, 0, 0, 0.3) !important; + display: block !important; + height: 100% !important; + pointer-events: none !important; + position: absolute !important; + width: 100% !important; +} + +/* Fixes shorts thumbnails */ +.ytlr-tile-header-renderer--shorts { + z-index: -1; +} + +/** + * Dirty hack to fix offset