Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix shorts #235

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
87 changes: 87 additions & 0 deletions src/screensaver-fix.ts
Original file line number Diff line number Diff line change
@@ -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<E extends typeof Element>(
cssSelectors: string,
expected: E
): Promise<InstanceType<E>> {
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<E>;
}

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<E>;
}

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']
});
5 changes: 5 additions & 0 deletions src/ui.css
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,8 @@
position: absolute !important;
width: 100% !important;
}

/* Fixes shorts thumbnails */
.ytlr-tile-header-renderer--shorts {
z-index: -1;
}
38 changes: 2 additions & 36 deletions src/userScript.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'whatwg-fetch';
import './domrect-polyfill';

import { handleLaunch, waitForChildAdd } from './utils';
import { handleLaunch } from './utils';

document.addEventListener(
'webOSRelaunch',
Expand All @@ -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';
Loading