YouTube Wide Mode + Tile Control

Control video scaleX on /watch and tile count

// ==UserScript==
// @name         YouTube Wide Mode + Tile Control
// @description  Control video scaleX on /watch and tile count
// @version      1.1
// @match        https://www.youtube.com/*
// @grant        GM_addStyle
// @license MIT
// @namespace https://gf.qytechs.cn/users/1029007
// ==/UserScript==

(function () {
    'use strict';

    let scaleX = 1.3;
    let tilesPerRow = 4;

    GM_addStyle(`
        html, body {
            scrollbar-width: none;
            -ms-overflow-style: none;
        }
        body::-webkit-scrollbar {
            display: none;
        }
        #yt-wide-control, #yt-tile-control {
            display: flex;
            align-items: center;
            gap: 6px;
            margin-right: 10px;
        }
        #yt-wide-control input, #yt-tile-control input {
            width: 60px;
        }
    `);

    function applyScale() {
        const video = document.querySelector('ytd-watch-flexy[theater] #movie_player video');
        if (video) {
            video.style.transform = `scaleX(${scaleX})`;
            video.style.transformOrigin = 'center center';
        }
    }

    // 👇 Постоянно следим за появлением <video>
    const videoObserver = new MutationObserver(() => {
        const video = document.querySelector('ytd-watch-flexy[theater] #movie_player video');
        if (video) {
            applyScale();
        }
    });

    function observeVideo() {
        const moviePlayer = document.querySelector('ytd-watch-flexy[theater] #movie_player');
        if (moviePlayer) {
            videoObserver.disconnect(); // на всякий случай перезапуск
            videoObserver.observe(moviePlayer, {
                childList: true,
                subtree: true,
            });
        }
    }

    function updateTiles() {
        let style = document.getElementById('tile-style');
        if (!style) {
            style = document.createElement('style');
            style.id = 'tile-style';
            document.head.appendChild(style);
        }
        style.textContent = `
            ytd-rich-grid-renderer {
                --ytd-rich-grid-items-per-row: ${tilesPerRow} !important;
            }
        `;
    }

    function preventScrollOnWheel(input) {
        input.addEventListener('wheel', e => {
            e.stopPropagation();
        }, { passive: false });
    }

    function waitForTargetElement(selector, callback) {
        const interval = setInterval(() => {
            const target = document.querySelector(selector);
            if (target) {
                clearInterval(interval);
                callback(target);
            }
        }, 300);
    }

    function createScaleInput(target) {
        if (document.getElementById('yt-wide-control')) return;

        const container = document.createElement('div');
        container.id = 'yt-wide-control';

        const scaleInput = document.createElement('input');
        scaleInput.type = 'number';
        scaleInput.min = '1.0';
        scaleInput.step = '0.01';
        scaleInput.value = scaleX;
        scaleInput.title = 'scaleX';
        scaleInput.oninput = (e) => {
            scaleX = parseFloat(e.target.value) || 1.0;
            applyScale();
        };
        preventScrollOnWheel(scaleInput);

        container.appendChild(scaleInput);
        target.insertBefore(container, target.firstChild);
    }

    function createTileInput(target) {
        if (document.getElementById('yt-tile-control')) return;

        const container = document.createElement('div');
        container.id = 'yt-tile-control';

        const tileInput = document.createElement('input');
        tileInput.type = 'number';
        tileInput.min = '1';
        tileInput.step = '1';
        tileInput.value = tilesPerRow;
        tileInput.title = 'tiles';
        tileInput.oninput = (e) => {
            tilesPerRow = parseInt(e.target.value) || 1;
            updateTiles();
        };
        preventScrollOnWheel(tileInput);

        container.appendChild(tileInput);
        target.insertBefore(container, target.firstChild);
    }

    function setup() {
        const path = window.location.pathname;
        const isWatch = path.startsWith('/watch');
        const isMainOrSubs = path === '/' || path === '/feed/subscriptions';

        waitForTargetElement('#end', (target) => {
            if (isWatch) {
                createScaleInput(target);
                applyScale();
                observeVideo(); // 👈 запускаем наблюдение за видео
            } else {
                const wideControl = document.getElementById('yt-wide-control');
                if (wideControl) wideControl.remove();
                videoObserver.disconnect(); // отключаем, если не /watch
            }

            if (isMainOrSubs) {
                createTileInput(target);
                updateTiles();
            } else {
                const tileControl = document.getElementById('yt-tile-control');
                if (tileControl) tileControl.remove();
            }
        });
    }

    const observer = new MutationObserver(setup);
    observer.observe(document.body, { childList: true, subtree: true });

    window.addEventListener('yt-navigate-finish', () => {
        setTimeout(setup, 300);
    });

    setup();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址