Greasy Fork is available in English.

YouTube FullWindow

Changes YouTube theater mode appearance to fullwindow, press T to enter, T or ESC to exit

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey to install this script.

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name              YouTube FullWindow
// @name:zh-CN        YouTube网页全屏
// @name:ja           YouTubeフルウィンドウ
// @namespace         http://tampermonkey.net/
// @version           1.4
// @description       Changes YouTube theater mode appearance to fullwindow, press T to enter, T or ESC to exit
// @description:zh-CN 将YouTube影院模式的外观改为网页全屏,按T进入,按T或ESC退出
// @description:ja    YouTubeシアターモードの外観をフルウィンドウに変更、Tで入り、TまたはESCで退出
// @author            jo
// @match             https://www.youtube.com/*
// @icon              https://www.youtube.com/favicon.ico
// @grant             none
// @license           MIT
// ==/UserScript==

(function () {
    'use strict';

    let isHandlingFullscreenFromTheater = false;
    let doubleClickHandler = null;

    function addFullscreenStyles() {
        if (document.getElementById('yt-web-fullscreen-style')) {
            return;
        }

        const style = document.createElement('style');
        style.id = 'yt-web-fullscreen-style';
        style.textContent = `
            /* 影院模式时的样式 */
            ytd-watch-flexy[theater] #full-bleed-container.ytd-watch-flexy,
            ytd-watch-flexy[full-bleed-player] #full-bleed-container.ytd-watch-flexy {
                position: fixed !important;
                top: 0 !important;
                left: 0 !important;
                z-index: 3000 !important;
                width: 100vw !important;
                height: 100vh !important;
                max-height: 100vh !important;
                background: #000 !important;
            }

            /* 全屏模式样式 */
            ytd-watch-flexy[fullscreen] #full-bleed-container.ytd-watch-flexy {
                position: fixed !important;
                top: 0 !important;
                left: 0 !important;
                z-index: 3000 !important;
                width: 100vw !important;
                height: 100vh !important;
                max-height: 100vh !important;
                background: #000 !important;
            }

            /* 影院模式下隐藏页面右侧滚动条(使用 :has 直接作用于 html / body) */
            html:has(ytd-watch-flexy[theater]),
            html:has(ytd-watch-flexy[full-bleed-player]) {
                overflow: hidden !important;
                height: 100% !important;
            }

            body:has(ytd-watch-flexy[theater]),
            body:has(ytd-watch-flexy[full-bleed-player]) {
                overflow: hidden !important;
                height: 100% !important;
            }

            /* 确保页面内容正常显示 */
            ytd-watch-flexy:not([full-bleed-player]):not([fullscreen]) {
                overflow: visible !important;
            }

            /* 强制显示影院模式按钮(全屏模式下) */
            .ytp-fullscreen .ytp-size-button {
                display: inline-block !important;
                opacity: 1 !important;
                visibility: visible !important;
            }

            /* 确保按钮在 hover 状态下也可见 */
            .ytp-fullscreen .ytp-size-button:hover {
                opacity: 1 !important;
            }
        `;
        document.head.appendChild(style);
        console.log('YouTube网页全屏样式已注入');
    }

    // 获取当前 watch 页面容器
    function getWatchFlexy() {
        return document.querySelector('ytd-watch-flexy');
    }

    // 使用 ytd-watch-flexy 属性检测当前模式
    function getCurrentMode() {
        const watchFlexy = getWatchFlexy();
        if (!watchFlexy) return 'default';

        // 优先识别全屏
        if (watchFlexy.hasAttribute('fullscreen')) return 'fullscreen';

        // 新版一般在 ytd-watch-flexy 上挂 theater / full-bleed-player
        if (watchFlexy.hasAttribute('theater') || watchFlexy.hasAttribute('full-bleed-player')) {
            return 'theater';
        }

        return 'default';
    }

    function toggleTheaterMode() {
        const theaterButton = document.querySelector('.ytp-size-button.ytp-button');
        if (theaterButton) {
            theaterButton.click();
            console.log('切换影院模式');
            return true;
        }
        return false;
    }

    function toggleFullscreen() {
        const fullscreenButton = document.querySelector('.ytp-fullscreen-button.ytp-button');
        if (fullscreenButton) {
            fullscreenButton.click();
            console.log('切换全屏模式');
            return true;
        }
        return false;
    }

    function exitTheaterMode() {
        const currentMode = getCurrentMode();
        if (currentMode === 'theater') {
            return toggleTheaterMode();
        }
        return false;
    }

    function handleKeyDown(event) {
        // 检查是否在输入框或文本区域中
        const activeElement = document.activeElement;
        const isTextInput = activeElement.tagName === 'INPUT' ||
            activeElement.tagName === 'TEXTAREA' ||
            activeElement.isContentEditable;

        if (isTextInput) {
            return;
        }

        const currentMode = getCurrentMode();

        // T键处理:切换影院模式(网页全屏)
        if (event.keyCode === 84) { // T key
            event.preventDefault();
            event.stopPropagation();
            event.stopImmediatePropagation();

            console.log('按T键,当前模式:', currentMode);

            if (currentMode === 'default') {
                // 默认模式 -> 进入影院模式
                toggleTheaterMode();
            } else if (currentMode === 'theater') {
                // 影院模式 -> 退出到默认模式
                toggleTheaterMode();
            } else if (currentMode === 'fullscreen') {
                // 全屏模式 -> 退出全屏,进入影院模式
                toggleFullscreen();
                setTimeout(() => {
                    if (getCurrentMode() === 'default') {
                        toggleTheaterMode();
                    }
                }, 100);
            }
        }

        // F键处理:切换全屏
        if (event.keyCode === 70) { // F key
            event.preventDefault();
            event.stopPropagation();
            event.stopImmediatePropagation();

            console.log('按F键,当前模式:', currentMode);

            if (currentMode === 'default') {
                // 默认模式 -> 进入全屏模式
                toggleFullscreen();
            } else if (currentMode === 'theater') {
                // 影院模式 -> 先退出影院模式,再进入全屏
                isHandlingFullscreenFromTheater = true;
                exitTheaterMode();
                setTimeout(() => {
                    toggleFullscreen();
                    isHandlingFullscreenFromTheater = false;
                }, 100);
            } else if (currentMode === 'fullscreen') {
                // 全屏模式 -> 退出全屏(交给 YouTube 内部逻辑处理返回状态)
                toggleFullscreen();
            }
        }

        // ESC键处理:退出到默认视图
        if (event.keyCode === 27) { // ESC key
            const currentMode = getCurrentMode();

            if (currentMode === 'fullscreen' || currentMode === 'theater') {
                event.preventDefault();
                event.stopPropagation();
                event.stopImmediatePropagation();

                console.log('按ESC键,退出到默认视图');

                if (currentMode === 'fullscreen') {
                    // 全屏模式 -> 退出全屏(交给 YouTube 内部逻辑处理返回状态)
                    toggleFullscreen();
                } else if (currentMode === 'theater') {
                    // 仅处于影院模式时,直接退出到默认视图
                    toggleTheaterMode();
                }
            }
        }
    }

    // 强制显示影院模式按钮
    function forceShowButtons() {
        const sizeButtons = document.querySelectorAll('.ytp-size-button');

        sizeButtons.forEach(button => {
            button.style.display = 'inline-block';
            button.style.opacity = '1';
            button.style.visibility = 'visible';
        });
    }

    // 监听全屏按钮点击,实现从影院模式进入全屏时的拆解操作
    function setupFullscreenButtonListener() {
        document.addEventListener('click', function (event) {
            const target = event.target;
            const fullscreenButton = target.closest('.ytp-fullscreen-button.ytp-button');

            const modeBeforeClick = getCurrentMode();

            if (fullscreenButton && modeBeforeClick === 'theater' && !isHandlingFullscreenFromTheater) {
                event.preventDefault();
                event.stopPropagation();
                event.stopImmediatePropagation();

                console.log('从影院模式点击全屏按钮,拆解操作');
                isHandlingFullscreenFromTheater = true;

                // 先退出影院模式,再进入全屏
                exitTheaterMode();
                setTimeout(() => {
                    toggleFullscreen();
                    isHandlingFullscreenFromTheater = false;
                }, 100);
            } else if (fullscreenButton) {
                // 默认模式下点击全屏按钮
                console.log('检测到全屏按钮点击');
                // 交给 YouTube 默认行为处理进入全屏
                // 延迟确保按钮在全屏模式下显示
                setTimeout(forceShowButtons, 200);
            }
        }, true);
    }

    // 拦截影院模式下的双击全屏
    function setupDoubleClickListener() {
        // 移除之前的监听器
        if (doubleClickHandler) {
            document.removeEventListener('dblclick', doubleClickHandler);
        }

        doubleClickHandler = function (event) {
            const currentMode = getCurrentMode();

            // 如果在影院模式下双击,拦截并执行拆解操作
            if (currentMode === 'theater' && !isHandlingFullscreenFromTheater) {
                event.preventDefault();
                event.stopPropagation();
                event.stopImmediatePropagation();

                console.log('拦截影院模式下双击全屏,执行拆解操作');
                isHandlingFullscreenFromTheater = true;

                // 先退出影院模式,再进入全屏
                exitTheaterMode();
                setTimeout(() => {
                    toggleFullscreen();
                    isHandlingFullscreenFromTheater = false;
                }, 100);
            }
        };

        // 使用捕获阶段确保我们先于YouTube处理双击事件
        document.addEventListener('dblclick', doubleClickHandler, true);
    }

    function handlePageChange() {
        console.log('检测到页面变化,重新初始化...');
        isHandlingFullscreenFromTheater = false;
        addFullscreenStyles();
        setTimeout(setupFullscreenButtonListener, 1000);
        setTimeout(setupDoubleClickListener, 1000);
    }

    // 初始化函数
    function init() {
        console.log('初始化YouTube网页全屏脚本');

        addFullscreenStyles();
        document.addEventListener('keydown', handleKeyDown, true);
        setupFullscreenButtonListener();
        setupDoubleClickListener();

        // 监听DOM变化(仅关注 watch 容器)
        const domObserver = new MutationObserver(function (mutations) {
            let shouldReinit = false;
            mutations.forEach(function (mutation) {
                if (mutation.type === 'childList') {
                    mutation.addedNodes.forEach(function (node) {
                        if (node.nodeType === 1 &&
                            node.tagName === 'YTD-WATCH-FLEXY') {
                            shouldReinit = true;
                        }
                    });
                }
            });
            if (shouldReinit) setTimeout(handlePageChange, 100);
        });
        domObserver.observe(document.body, { childList: true, subtree: true });
    }

    // 监听URL变化
    let lastUrl = location.href;
    const urlObserver = new MutationObserver(() => {
        const url = location.href;
        if (url !== lastUrl) {
            lastUrl = url;
            handlePageChange();
        }
    });
    urlObserver.observe(document, { subtree: true, childList: true });

    // 初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();