YouTube FullWindow

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

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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.3
// @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 isWebFullscreen = false;
    let mastheadObserver = null;
    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[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;
            }

            /* 隐藏页面右侧滚动条 */
            .web-fullscreen-active {
                overflow: hidden !important;
                height: 100% !important;
            }

            .web-fullscreen-active body {
                overflow: hidden !important;
                height: 100% !important;
            }

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

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

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

    function toggleFullscreenState(enable) {
        if (enable === isWebFullscreen) return;

        if (enable) {
            isWebFullscreen = true;
            document.documentElement.classList.add('web-fullscreen-active');
            console.log('进入网页全屏模式');
        } else {
            isWebFullscreen = false;
            document.documentElement.classList.remove('web-fullscreen-active');
            console.log('退出网页全屏模式');
        }
    }

    // 使用masthead属性检测当前模式
    function getCurrentMode() {
        const masthead = document.getElementById('masthead');
        if (!masthead) return 'default';

        if (masthead.hasAttribute('fullscreen')) return 'fullscreen';
        if (masthead.hasAttribute('theater')) 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') {
                // 全屏模式 -> 退出全屏
                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') {
                    toggleFullscreen();
                } else if (currentMode === 'theater') {
                    toggleTheaterMode();
                }
            }
        }
    }

    function observeMastheadChanges() {
        if (mastheadObserver) {
            mastheadObserver.disconnect();
        }

        const masthead = document.getElementById('masthead');
        if (!masthead) {
            setTimeout(observeMastheadChanges, 1000);
            return;
        }

        let lastMode = getCurrentMode();

        mastheadObserver = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                if (mutation.type === 'attributes' &&
                    (mutation.attributeName === 'theater' || mutation.attributeName === 'fullscreen')) {

                    const currentMode = getCurrentMode();
                    console.log('模式变化:', currentMode, '之前模式:', lastMode);

                    // 更新最后模式
                    lastMode = currentMode;

                    // 影院模式或全屏模式都启用网页全屏样式
                    toggleFullscreenState(currentMode !== 'default');

                    // 全屏模式下强制显示按钮
                    if (currentMode === 'fullscreen') {
                        setTimeout(forceShowButtons, 100);
                    }
                }
            });
        });

        mastheadObserver.observe(masthead, {
            attributes: true,
            attributeFilter: ['theater', 'fullscreen']
        });

        // 初始状态检查
        const currentMode = getCurrentMode();
        console.log('初始模式:', currentMode);
        lastMode = currentMode;
        toggleFullscreenState(currentMode !== 'default');
    }

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

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

        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');

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

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

                // 先退出影院模式,再进入全屏
                exitTheaterMode();
                setTimeout(() => {
                    toggleFullscreen();
                    isHandlingFullscreenFromTheater = false;
                }, 100);
            } else if (fullscreenButton) {
                // 普通情况下的全屏按钮点击
                console.log('检测到全屏按钮点击');
                // 延迟确保按钮在全屏模式下显示
                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('检测到页面变化,重新初始化...');
        toggleFullscreenState(false);
        isHandlingFullscreenFromTheater = false;
        addFullscreenStyles();
        setTimeout(observeMastheadChanges, 500);
        setTimeout(setupFullscreenButtonListener, 1000);
        setTimeout(setupDoubleClickListener, 1000);
    }

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

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

        // 监听DOM变化
        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.id === 'masthead') {
                            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();
    }
})();