YouTube Sizer

Resizes the YouTube player to different sizes

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name YouTube Sizer
// @author John Burt
// @namespace namespace_runio
// @version 2.04
// @description Resizes the YouTube player to different sizes
// @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-end
// @grant GM_setValue
// @grant GM_getValue
// @supportURL https://greasyfork.org/scripts/421396-youtube-sizer
// @icon https://i.imgur.com/9haPE5X.png
// @license GPL-3.0+
// @noframes
// ==/UserScript==
(function() {
    "use strict";

    if (window.frameElement) {
        throw new Error("Stopped JavaScript.");
    }
    //==================================================================
    // Storage helpers
    //==================================================================
    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 || value === undefined) {
            setPref(preference, new_value);
            value = new_value;
        } else if (typeof value === "number" && isNaN(value)) {
            console.warn(`[YT Sizer] Stored preference "${preference}" was NaN — resetting to default.`);
            setPref(preference, new_value);
            value = new_value;
        }
        return value;
    }
    //==================================================================
    // Preferences
    //==================================================================
    initPref("yt-width", 1280);
    initPref("yt-resize", false);

    function getMaxWidth() {
        const stored = getPref("yt-width") ?? 854;
        return Math.max(854, Math.min(stored, window.innerWidth));
    }

    function setMaxWidth(value) {
        const clamped = Math.max(854, Math.min(value, window.innerWidth));
        setPref("yt-width", clamped);
    }
    //==================================================================
    // Global constants / state
    //==================================================================
    var shortcutKey = "R";
    var ytresizeCss = `ytd-watch-flexy[fullscreen] #ytp-resize-button { display:none !important; }`;
    let currentResizeObserver = null;
    let keyListenersAdded = false;
    let sizeObserverInstance = null;
    let resizeDebounceTimer = null;
    let checkURLDebounceTimer = null;
    let lastCheckedPath = "";
    //==================================================================
    // Boot
    //==================================================================
    window.addEventListener("load", () => {
        const observer = new MutationObserver(checkURL);
        const titleEl = document.querySelector("title");
        if (titleEl) {
            observer.observe(titleEl, {
                attributes: true,
                characterData: true,
                childList: true
            });
            checkURL();
        }
    }, {
        once: true
    });
    //==================================================================
    // checkURL
    //==================================================================
    function checkURL() {
        clearTimeout(checkURLDebounceTimer);
        checkURLDebounceTimer = setTimeout(() => {
            const path = window.location.pathname;
            if (!path.includes("watch")) return;
            if (path === lastCheckedPath) return;
            lastCheckedPath = path;
            waitElement("#player-container-outer").then((elm) => {
                if (!window.location.pathname.includes("watch")) return;
                if (!document.getElementById("yt-css")) {
                    startMethods();
                } else {
                    controlResize();
                    createResize();
                    viewObserver();
                }
            }).catch(() => {});
        }, 150);
    }
    //==================================================================
    // waitElement
    //==================================================================
    function waitElement(selector, timeoutMs = 5000) {
        return new Promise((resolve, reject) => {
            let element = document.querySelector(selector);
            if (element) return resolve(element);
            const observer = new MutationObserver(() => {
                element = document.querySelector(selector);
                if (element) {
                    clearTimeout(timer);
                    observer.disconnect();
                    resolve(element);
                }
            });
            const timer = setTimeout(() => {
                observer.disconnect();
                reject(new Error(`waitElement: "${selector}" not found within ${timeoutMs} ms`));
            }, timeoutMs);
            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        });
    }
    //==================================================================
    // startMethods
    //==================================================================
    function startMethods() {
        sizeObserver();
        if (getPref("yt-resize") === true) {
            addCss(`#primary.ytd-watch-flexy:not([theater]):not([fullscreen]) { max-width: ${getMaxWidth()}px !important; }`, "small-player");
            addCss(`ytd-watch-flexy[flexy]:not([full-bleed-player][full-bleed-no-max-width-columns]) #columns.ytd-watch-flexy {max-width: 100% !important;}`, "max-player");
        }
        addCss(ytresizeCss, "yt-css");
        controlResize();
        viewObserver();
    }
    //==================================================================
    // Helpers
    //==================================================================
    function isCentered(element1, element2) {
        const box1 = element1.getBoundingClientRect();
        const box2 = element2.getBoundingClientRect();
        const center1 = {
            x: box1.left + box1.width / 2,
            y: box1.top + box1.height / 2
        };
        const center2 = {
            x: box2.left + box2.width / 2,
            y: box2.top + box2.height / 2
        };
        return Math.abs(center1.x - center2.x) <= 1 &&
            Math.abs(center1.y - center2.y) <= 1;
    }

    function addCss(cssString, id) {
        const css = document.createElement("style");
        css.type = "text/css";
        css.id = id;
        css.textContent = cssString;
        document.head.appendChild(css);
    }
    //==================================================================
    // createResize
    //==================================================================
    function createResize() {
        const element = document.querySelector("ytd-app");
        if (!element) return;
        element.dispatchEvent(new CustomEvent("yt-action", {
            bubbles: true,
            cancelable: true,
            composed: true,
            detail: {
                actionName: "yt-window-resized",
                disableBroadcast: false,
                optionalAction: true,
                returnValue: []
            }
        }));
    }
    //==================================================================
    // viewObserver
    //==================================================================
    function viewObserver() {
        let movie_player = document.querySelector(".html5-video-player");
        let video = document.querySelector("video");
        if (!movie_player || !video) return;
        if (currentResizeObserver) currentResizeObserver.disconnect();
        const resizeObserver = new ResizeObserver((entries) => {
            window.requestAnimationFrame(() => {
                if (!Array.isArray(entries) || !entries.length) return;
                const currentlyCentered = isCentered(video, movie_player);
                if (!currentlyCentered) {
                    clearTimeout(resizeDebounceTimer);
                    resizeDebounceTimer = setTimeout(createResize, 100);
                }
            });
        });
        currentResizeObserver = resizeObserver;
        resizeObserver.observe(video);
    }
    //==================================================================
    // sizeObserver
    //==================================================================
    function sizeObserver() {
        if (sizeObserverInstance) return;
        const config = {
            attributes: true,
            childList: true,
            subtree: true,
            characterData: true
        };
        const callback = function(mutationsList) {
            for (let mutation of mutationsList) {
                const removedHasSmall = Array.from(mutation.removedNodes).some(
                    node => node.nodeType === Node.ELEMENT_NODE && node.id === "small-player"
                );
                const addedHasSmall = Array.from(mutation.addedNodes).some(
                    node => node.nodeType === Node.ELEMENT_NODE && node.id === "small-player"
                );
                if (removedHasSmall) {
                    setPref("yt-resize", false);
                    controlResize();
                    createResize();
                } else if (addedHasSmall) {
                    setPref("yt-resize", true);
                    controlResize();
                    createResize();
                }
                if (mutation.target && mutation.target.id === "small-player") {
                    setPref("yt-width", getMaxWidth());
                    createResize();
                } else if (mutation.target && mutation.target.parentNode &&
                    mutation.target.parentNode.id === "small-player") {
                    setPref("yt-width", getMaxWidth());
                    createResize();
                }
            }
        };
        sizeObserverInstance = new MutationObserver(callback);
        sizeObserverInstance.observe(document.head, config);
    }
    //==================================================================
    // Tooltip — R is now inside a real bordered box
    //==================================================================
    let resizeTooltipHideTimeout = null;

    function showResizeButtonTooltip(btn, show = true) {
        if (resizeTooltipHideTimeout) {
            clearTimeout(resizeTooltipHideTimeout);
            resizeTooltipHideTimeout = null;
        }

        const buttonRect = btn.getBoundingClientRect();
        const tooltipHorizontalCenter = buttonRect.left + buttonRect.width / 2;

        const tooltip = document.getElementById("ytd-resize-tt") || createTooltip();
        const tooltipText = tooltip.querySelector("#ytd-resize-tt-text");
        const tooltipKey = tooltip.querySelector("#ytd-resize-tt-key");

        if (show) {
            const label = btn.getAttribute("aria-label") || btn.getAttribute("title") || "Resize";
            tooltipText.textContent = label.replace(/\s*\[[^\]]+\]\s*$/, "");
            tooltipKey.textContent = shortcutKey;

            tooltip.style.removeProperty("display");
            tooltip.style.visibility = "hidden";
            tooltip.style.transition = "none";
            void tooltip.offsetHeight;

            const tooltipRect = tooltip.getBoundingClientRect();

            // --- FIX: anchor to stable YouTube UI baseline ---
            const controlsBar =
                  document.querySelector(".ytp-chrome-bottom") ||
                  document.querySelector(".ytp-progress-bar-container");
            let baseTop;
            if (controlsBar) {
                const controlsRect = controlsBar.getBoundingClientRect();
                baseTop = controlsRect.top;
            } else {
                baseTop = buttonRect.top;
            }

            const gap = 11.5;
            const tooltipTop = baseTop - tooltipRect.height - gap;

            tooltip.style.top = `${tooltipTop}px`;
            tooltip.style.left = `${tooltipHorizontalCenter - tooltipRect.width / 2}px`;
            tooltip.style.visibility = "visible";
            btn.removeAttribute("title");
        } else {
            resizeTooltipHideTimeout = setTimeout(() => {
                tooltip.style.display = "none";
                tooltip.style.visibility = "";
                tooltipText.textContent = "";
                tooltipKey.textContent = "";
                const currentLabel = btn.getAttribute("aria-label");
                if (currentLabel) {
                    btn.setAttribute("title", currentLabel);
                }
                resizeTooltipHideTimeout = null;
            }, 120);
        }

        function createTooltip() {
            const htmlPlayer = document.querySelector(".html5-video-player");
            if (!htmlPlayer) {
                const fallback = document.createElement("div");
                fallback.style.display = "none";
                return fallback;
            }

            const tooltip = document.createElement("div");
            tooltip.id = "ytd-resize-tt";
            tooltip.className = "ytp-tooltip ytp-bottom";
            tooltip.style.position = "fixed";
            tooltip.style.zIndex = "10000";

            const wrapper = document.createElement("div");
            wrapper.className = "ytp-tooltip-text-wrapper ytp-tooltip-bottom-text";

            const tooltipText = document.createElement("span");
            tooltipText.className = "ytp-tooltip-text";
            tooltipText.id = "ytd-resize-tt-text";

            const tooltipKey = document.createElement("span");
            tooltipKey.id = "ytd-resize-tt-key";

            wrapper.appendChild(tooltipText);
            wrapper.appendChild(tooltipKey);
            tooltip.appendChild(wrapper);

            if (!document.getElementById("yt-sizer-tooltip-style")) {
                const style = document.createElement("style");
                style.id = "yt-sizer-tooltip-style";
                style.textContent = `
                #ytd-resize-tt .ytp-tooltip-text-wrapper {
                    display: flex;
                    align-items: center;
                }
                #ytd-resize-tt-key {
                    display: inline-flex;
                    align-items: center;
                    justify-content: center;
                    min-width: 15px;
                    height: 15px;
                    margin-left: 4px;
                    padding: 0 4px;
                    border: 1px solid rgba(255,255,255,0.30);
                    border-radius: 4px;
                    font-size: 12px;
                    font-weight: 500;
                    line-height: 15px;
                    box-sizing: border-box;
                    vertical-align: middle;
                }
            `;
                document.head.appendChild(style);
            }

            htmlPlayer.appendChild(tooltip);
            return tooltip;
        }
    }
    //==================================================================
    // Button setup
    //==================================================================
    function setButton(btn, path) {
        let pathData = {};
        let ariaLabel, titleText;
        if (getPref("yt-resize") !== true) {
            pathData.d = `M 13 17 L 5 9 L 5 17 Z
                          M 23 19
                          L 23 4.98 C 23 3.88 22.1 3 21 3
                          L 3 3 C 1.9 3 1 3.88 1 4.98
                          L 1 19 C 1 20.1 1.9 21 3 21
                          L 21 21 C 22.1 21 23 20.1 23 19
                          L 23 19 Z
                          M 21 19.02 L 3 19.02 L 3 4.97
                          L 21 4.97 L 21 19.02 L 21 19.02 Z`;
            ariaLabel = `Resize mode [${shortcutKey}]`;
            titleText = `Resize mode [${shortcutKey}]`;
        } else {
            pathData.d = `M 19 15 L 19 7 L 11 7 Z M 23 19
                          L 23 4.98 C 23 3.88 22.1 3 21 3
                          L 3 3 C 1.9 3 1 3.88 1 4.98
                          L 1 19 C 1 20.1 1.9 21 3 21
                          L 21 21 C 22.1 21 23 20.1 23 19
                          L 23 19 Z M 21 19.02
                          L 3 19.02 L 3 4.97
                          L 21 4.97 L 21 19.02 L 21 19.02 Z`;
            ariaLabel = `Default view [${shortcutKey}]`;
            titleText = `Default view [${shortcutKey}]`;
        }
        path.setAttribute("d", pathData.d);
        btn.setAttribute("aria-label", ariaLabel);
        btn.setAttribute("title", titleText);
    }

    function createButton(container) {
        const btn = document.createElement("button");
        const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
        svg.setAttribute("height", "24");
        svg.setAttribute("viewBox", "0 0 24 24");
        svg.setAttribute("width", "24");
        setButton(btn, path);
        path.setAttribute("fill", "white");
        svg.appendChild(path);
        btn.appendChild(svg);
        btn.classList.add("ytp-resize-button", "ytp-button");
        btn.setAttribute("id", "ytp-resize-button");
        btn.setAttribute("data-tooltip-target-id", "ytp-resize-button");
        container.insertBefore(btn, container.lastChild.previousSibling || container.lastChild);
        const showTooltip = (event) => {
            showResizeButtonTooltip(btn, ["mouseover", "focus"].includes(event.type));
        };
        btn.addEventListener("click", (e) => {
            e.stopPropagation();
            e.preventDefault();
            buttonScript();
        }, false);
        btn.addEventListener("mouseover", showTooltip);
        btn.addEventListener("mouseout", showTooltip);
        btn.addEventListener("focus", showTooltip);
        btn.addEventListener("blur", showTooltip);
    }

    function toggleStyle(id, cssTemplate) {
        const styleElement = document.getElementById(id);
        if (styleElement && document.head.contains(styleElement)) {
            document.head.removeChild(styleElement);
        } else {
            addCss(cssTemplate, id);
        }
    }

    function buttonScript() {
        toggleStyle(
            "max-player",
            `ytd-watch-flexy[flexy]:not([full-bleed-player][full-bleed-no-max-width-columns])
        #columns.ytd-watch-flexy { max-width: 100% !important; }`
        );
        toggleStyle(
            "small-player",
            `#primary.ytd-watch-flexy:not([theater]):not([fullscreen]) {
        max-width: ${getMaxWidth()}px !important; }`
        );
    }

    function shortScript() {
        const css = `#primary.ytd-watch-flexy:not([theater]):not([fullscreen]) { max-width: ${getMaxWidth()}px !important; }`;
        let splayer = document.getElementById("small-player");
        if (splayer && document.head.contains(splayer)) {
            splayer.textContent = css;
        } else {
            addCss(css, "small-player");
        }
    }
    //==================================================================
    // Keyboard / wheel handlers
    //==================================================================
    function handleKeydown(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;
        const splayer = document.getElementById("small-player");
        if (e.key === shortcutKey.toLowerCase() || e.key === shortcutKey.toUpperCase()) {
            e.stopPropagation();
            e.preventDefault();
            buttonScript();
            return;
        }
        if (document.head.contains(splayer)) {
            if (e.key === "z") {
                e.stopPropagation();
                e.preventDefault();
                setMaxWidth(getMaxWidth() - 20);
                shortScript();
            } else if (e.key === "x") {
                e.stopPropagation();
                e.preventDefault();
                setMaxWidth(getMaxWidth() + 20);
                shortScript();
            }
        }
    }

    function handleWheel(e) {
        const splayer = document.getElementById("small-player");
        if (!document.head.contains(splayer)) return;
        if (e.altKey || e.ctrlKey || e.metaKey) return;
        if (/^(?:input|textarea|select|button)$/i.test(e.target.tagName)) return;
        if (/(?:contenteditable-root)/i.test(e.target.id)) return;
        if (!e.shiftKey) return;
        e.stopPropagation();
        e.preventDefault();
        if (e.deltaY < 0) {
            setMaxWidth(getMaxWidth() + 20);
        } else if (e.deltaY > 0) {
            setMaxWidth(getMaxWidth() - 20);
        }
        shortScript();
    }
    //==================================================================
    // controlResize
    //==================================================================
    function addListenersOnce() {
        if (keyListenersAdded) return;
        document.addEventListener("keydown", handleKeydown, false);
        document.addEventListener("wheel", handleWheel, {
            passive: false
        });
        keyListenersAdded = true;
    }

    function controlResize() {
        const buttonExists = document.getElementById("ytp-resize-button");
        if (!buttonExists) {
            const container = document.querySelector(".ytp-right-controls-right") ||
                document.querySelector(".ytp-right-controls");
            if (container) {
                createButton(container);
                addListenersOnce();
            } else {
                waitElement(".ytp-right-controls-right, .ytp-right-controls").then((container) => {
                    if (!document.getElementById("ytp-resize-button")) {
                        createButton(container);
                    }
                    addListenersOnce();
                }).catch(() => {});
            }
        } else {
            setButton(buttonExists, buttonExists.querySelector("path"));
        }
    }
    //==================================================================
})();