/*
Resize the YouTube player to 480px size.
Copyright (C) 2023 Runio
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
// ==UserScript==
// @name YouTube Sizer
// @author Runio
// @namespace namespace_runio
// @version 1.53
// @description Make YouTube Player 480px Size
// @match https://www.youtube.com/*
// @match https://www.youtu.be/*
// @exclude https://www.youtube.com/tv*
// @exclude https://www.youtube.com/embed/*
// @exclude https://www.youtube.com/live_chat*
// @exclude https://www.youtube.com/shorts/*
// @run-at document-idle
// @grant GM_setValue
// @grant GM_getValue
// @icon https://i.imgur.com/KJeLd60.png
// @license GPL-3.0+
// @noframes
// ==/UserScript==
(function() {
"use strict";
//==================================================================
//Local Storage Functions
if (window.frameElement) throw new Error("Stopped JavaScript.");
function setPref(preference, new_value) {
GM_setValue(preference, new_value);
}
function getPref(preference) {
return GM_getValue(preference);
}
function initPref(preference, new_value) {
let value = getPref(preference);
if (value === null) {
setPref(preference, new_value);
value = new_value;
}
return value;
}
//==================================================================
// Global Variables
var maxWidth = 480; // Max Width of Video
var aspectRatio = 16 / 9; // Aspect Ratio
var shortcutKey = "r"; // Shortcut Key
var smallerCss = `#primary.ytd-watch-flexy:not([theater]):not([fullscreen]) {
max-width: calc(${maxWidth}px * ${aspectRatio}) !important;
}`;
var ytresizeCss = `.ytp-big-mode .ytp-chrome-controls .ytp-resize-button {
display: none !important;
}
.ytp-chrome-bottom {
max-width: calc(100% - 24px);width: calc(100% - 24px) !important;
}`;
//==================================================================
initPref("yt-resize", false);
window.addEventListener("yt-navigate-finish", () => {
setTimeout(startMethods, 500);
}, {
once: true
});
//==================================================================
function startMethods() {
const outer = document.getElementById("player-container-outer");
if (outer !== "null") {
sizeObserver(); // Size Observer
if (getPref("yt-resize")) {
addCss(smallerCss, "small-player");
}
addCss(ytresizeCss, "yt-css");
controlResize(); // Create Resize Button
viewInterval(); // Resize Interval
} else {
alert("player-container-outer not found");
}
}
//==================================================================
function isInViewport(video, element) {
let rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom >= video.clientHeight &&
rect.right >= video.clientWidth
);
}
function isCentered(element1, element2) {
let rect = element1.getBoundingClientRect();
let rect2 = element2.getBoundingClientRect();
let centered = {
outer: rect.left + rect.width / 2,
inner: rect2.left + rect2.width / 2,
};
return (
Math.floor(centered.outer) == Math.floor(centered.inner)
);
}
function addCss(cssString, id) {
const css = document.createElement('style');
css.type = "text/css";
css.id = id;
css.innerHTML = cssString;
document.head.appendChild(css);
}
//==================================================================
/*Resize Video Container*/
function createResize() {
const ytd_video = document.getElementById("ytd-player");
const movie_player = document.getElementById("movie_player");
const {
clientHeight: height,
clientWidth: width
} = movie_player;
if (typeof ytd_video !== null && typeof movie_player !== null) {
ytd_video.player_.setInternalSize(width, height);
}
return;
}
/* Check Video Container */
function viewInterval() {
const html_video = document.querySelector(".video-stream.html5-main-video");
const movie_player = document.getElementById("movie_player");
const video = document.getElementsByTagName("video")[0];
const ytd_flexy = document.getElementsByTagName("ytd-watch-flexy")[0];
const chrome_bottom = document.querySelector(".ytp-chrome-bottom");
setInterval(() => {
const isInViewportYTD = isInViewport(video, ytd_flexy);
const isCenteredMoviePlayer = isCentered(video, movie_player);
const isInViewportHTMLPlayer = isInViewport(chrome_bottom, html_video);
const isCenteredHTMLPlayer = isCentered(chrome_bottom, html_video);
if (isInViewportYTD && isInViewportHTMLPlayer && !isCenteredMoviePlayer && !isCenteredHTMLPlayer) {
const height = video.clientHeight; // Height Of The Video Element
if (height !== maxWidth) {
createResize();
console.log(`Player Size: ${video.clientHeight} x ${video.clientWidth}`);
}
}
}, 500);
}
//==================================================================
/*Saves Size Setting*/
function sizeObserver() {
const targetNode = document.head;
const config = {
attributes: true,
childList: true,
subtree: true
};
const callback = function(mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.removedNodes.length >= 1 && mutation.removedNodes[0].id == "small-player") {
setPref("yt-resize", false); // Set Resize To False
controlResize(); // Change Button Icon
createResize(); // Resize Video Container
} else if (mutation.addedNodes.length >= 1 && mutation.addedNodes[0].id == "small-player") {
setPref("yt-resize", true); // Set Resize To True
controlResize(); // Change Button Icon
createResize(); // Resize Video Container
}
}
};
const observer = new MutationObserver(callback);
observer.observe(targetNode, config);
}
//==================================================================
function showResizeButtonTooltip(btn, show = true) {
let tooltipTopOffset = 62; // Height Above The Button For The Tooltip
const buttonRect = btn.getBoundingClientRect(); // Get Button Position
const tooltipHorizontalCenter = buttonRect.left + buttonRect.width / 2; // Tooltip Horizontal Center
const tooltipTop = buttonRect.top + buttonRect.height / 2 - tooltipTopOffset; // Tooltip Top
const tooltip = document.getElementById("ytd-resize-tt") || createTooltip();
const tooltipText = tooltip.querySelector('#ytd-resize-tt-text');
if (show) { // Show
tooltip.style.top = `${tooltipTop}px`;
tooltipText.textContent = btn.getAttribute("aria-label");
tooltip.style.removeProperty("display");
const tooltipWidth = tooltip.getBoundingClientRect().width;
tooltip.style.left = `${tooltipHorizontalCenter - tooltipWidth / 2}px`;
btn.removeAttribute("title");
} else { // Hide
tooltip.style.setProperty("display", "none");
tooltipText.textContent = "";
btn.setAttribute("title", btn.getAttribute("aria-label"));
}
function createTooltip() {
const htmlPlayer = document.querySelector(".html5-video-player");
const tooltip = document.createElement("div");
const tooltipTextWrapper = document.createElement("div");
const tooltipText = document.createElement("span");
tooltip.setAttribute("class", "ytp-tooltip ytp-bottom");
tooltip.setAttribute("id", "ytd-resize-tt");
tooltip.style.setProperty("position", "fixed");
tooltipTextWrapper.setAttribute("class", "ytp-tooltip-text-wrapper");
tooltipText.setAttribute("class", "ytp-tooltip-text");
tooltipText.setAttribute("id", "ytd-resize-tt-text");
tooltip.appendChild(tooltipTextWrapper);
tooltipTextWrapper.appendChild(tooltipText);
htmlPlayer.appendChild(tooltip);
return tooltip;
}
}
//==================================================================
/*Resize Button Script*/
function buttonScript() {
let splayer = document.getElementById("small-player");
if (document.head.contains(splayer)) {
document.head.removeChild(splayer);
} else {
addCss(smallerCss, "small-player");
}
return;
}
function setButton(btn, path) {
var pathData = {};
var ariaLabel = "";
var title = "";
if (!getPref("yt-resize")) {
pathData.d = "M 18 24 L 10 16 L 10 24 Z M 29 25 L 29 10.98 C 29 9.88 28.1 9 27 9 L 9 9 C 7.9 9 7 9.88 7 10.98 L 7 25 C 7 26.1 7.9 27 9 27 L 27 27 C 28.1 27 29 26.1 29 25 L 29 25 Z M 27 25.02 L 9 25.02 L 9 10.97 L 27 10.97 L 27 25.02 L 27 25.02 Z";
ariaLabel = `Resize View (${shortcutKey})`;
title = `Resize View (${shortcutKey})`;
} else {
pathData.d = "M 26 20 L 26 12 L 18 12 Z M 29 25 L 29 10.98 C 29 9.88 28.1 9 27 9 L 9 9 C 7.9 9 7 9.88 7 10.98 L 7 25 C 7 26.1 7.9 27 9 27 L 27 27 C 28.1 27 29 26.1 29 25 L 29 25 Z M 27 25.02 L 9 25.02 L 9 10.97 L 27 10.97 L 27 25.02 L 27 25.02 Z";
ariaLabel = `Default View (${shortcutKey})`;
title = `Default View (${shortcutKey})`;
}
path.setAttribute("d", pathData.d);
btn.setAttribute("aria-label", ariaLabel);
btn.setAttribute("title", title);
}
function createButton() {
var abtn = document.getElementsByClassName("ytp-right-controls")[0];
var btn = document.createElement("button");
var path = document.createElement("path");
var clickEvent = new Event('click', {
bubbles: false
});
/*Start Create SVG*/
var svg = document.createElement("svg");
svg.setAttribute("height", "100%");
svg.setAttribute("version", "1.1");
svg.setAttribute("viewBox", "0 0 36 36");
svg.setAttribute("width", "100%");
var use = document.createElement("use");
use.setAttribute("class", "ytp-svg-shadow");
setButton(btn, path); // Decide Which Button
path.setAttribute("fill", "#fff");
path.setAttribute("fill-rule", "evenodd");
svg.appendChild(use);
svg.appendChild(path);
const btnContent = svg.outerHTML;
/*Finished Create SVG*/
btn.innerHTML = btnContent;
btn.classList.add("ytp-resize-button", "ytp-button");
btn.setAttribute("id", "ytp-resize-button");
btn.setAttribute("data-tooltip-target-id", "ytp-resize-button");
abtn.insertBefore(btn, abtn.lastChild.previousSibling);
/*Tooltip Event Handlers*/
const showTooltip = (event) => {
const isMouseOver = ["mouseover", "focus"].includes(event.type);
showResizeButtonTooltip(btn, isMouseOver);
};
btn.addEventListener('click', function(e) {
e.stopPropagation();
e.preventDefault();
buttonScript();
}, clickEvent);
btn.addEventListener("mouseover", showTooltip);
btn.addEventListener("mouseout", showTooltip);
btn.addEventListener("focus", showTooltip);
btn.addEventListener("blur", showTooltip);
}
/*Create Resize Button*/
function controlResize() {
var buttonExists = document.getElementById("ytp-resize-button");
if (!buttonExists) {
window.addEventListener("keydown", function(e) {
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return;
if (/^(?:input|textarea|select|button)$/i.test(e.target.tagName)) return;
if (/(?:contenteditable-root)/i.test(e.target.id)) return;
if (e.key == shortcutKey.toLowerCase() || e.key == shortcutKey.toUpperCase()) {
e.stopPropagation();
e.preventDefault();
buttonScript();
}
return;
});
createButton();
} else {
setButton(buttonExists, buttonExists.querySelector('path'));
}
}
//==================================================================
})();