YouTube网页全屏

将YouTube影院模式的外观改为网页全屏,按T进入,按T或ESC退出

当前为 2025-09-27 提交的版本,查看 最新版本

// ==UserScript==
// @name              YouTube FullWindow
// @name:zh-CN        YouTube网页全屏
// @name:ja           YouTubeフルウィンドウ
// @namespace         http://tampermonkey.net/
// @version           1.2
// @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 theaterModeObserver = null;
    let wasInFullscreenFromTheater = false; // 标记是否从影院模式进入全屏

    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;
            }
        `;
        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('退出网页全屏模式');
        }
    }

    function getCurrentMode() {
        const watchFlexy = document.querySelector('ytd-watch-flexy');
        if (!watchFlexy) return 'default';

        if (watchFlexy.hasAttribute('fullscreen')) return 'fullscreen';
        if (watchFlexy.hasAttribute('full-bleed-player')) return 'theater';
        return 'default';
    }

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

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

    function exitToDefaultView() {
        const currentMode = getCurrentMode();
        console.log('退出到默认视图,当前模式:', currentMode);

        if (currentMode === 'fullscreen') {
            // 记录是从影院模式进入全屏的
            wasInFullscreenFromTheater = (getCurrentMode() === 'fullscreen' &&
                                          document.querySelector('ytd-watch-flexy[full-bleed-player]') !== null);

            // 先退出全屏
            if (exitFullscreen()) {
                // 延迟检查是否需要退出影院模式
                setTimeout(() => {
                    if (getCurrentMode() === 'theater') {
                        exitTheaterMode();
                    }
                    wasInFullscreenFromTheater = false; // 重置标记
                }, 150);
            }
        } else if (currentMode === 'theater') {
            // 直接退出影院模式
            exitTheaterMode();
        }
    }

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

        if (isTextInput) {
            return;
        }

        const currentMode = getCurrentMode();

        // 全屏模式下按ESC或F键都退出到默认视图
        if (currentMode === 'fullscreen' && (event.keyCode === 27 || event.keyCode === 70)) {
            event.preventDefault();
            event.stopPropagation();
            event.stopImmediatePropagation();

            console.log('全屏模式下按键:', event.keyCode === 27 ? 'ESC' : 'F');
            exitToDefaultView();
        }
        // 影院模式下只处理ESC键
        else if (currentMode === 'theater' && event.keyCode === 27) {
            event.preventDefault();
            event.stopPropagation();
            event.stopImmediatePropagation();

            console.log('影院模式下按ESC');
            exitToDefaultView();
        }
    }

    function observeModeChanges() {
        if (theaterModeObserver) {
            theaterModeObserver.disconnect();
        }

        const watchFlexy = document.querySelector('ytd-watch-flexy');
        if (!watchFlexy) {
            setTimeout(observeModeChanges, 1000);
            return;
        }

        let lastMode = getCurrentMode();

        theaterModeObserver = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                if (mutation.type === 'attributes' &&
                    (mutation.attributeName === 'full-bleed-player' || mutation.attributeName === 'fullscreen')) {

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

                    // 检测是否从全屏退出到影院模式
                    if (lastMode === 'fullscreen' && currentMode === 'theater') {
                        console.log('检测到从全屏退出到影院模式,自动退出到默认视图');
                        setTimeout(() => {
                            exitTheaterMode();
                        }, 100);
                    }

                    // 检测是否从影院模式进入全屏
                    if (lastMode === 'theater' && currentMode === 'fullscreen') {
                        wasInFullscreenFromTheater = true;
                        console.log('从影院模式进入全屏,标记状态');
                    }

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

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

        theaterModeObserver.observe(watchFlexy, {
            attributes: true,
            attributeFilter: ['full-bleed-player', 'fullscreen']
        });

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

    // 监听全屏按钮点击事件
    function setupFullscreenButtonListener() {
        document.addEventListener('click', function(event) {
            const target = event.target;
            const fullscreenButton = target.closest('.ytp-fullscreen-button.ytp-button');

            if (fullscreenButton) {
                console.log('检测到全屏按钮点击');
                // 标记是从影院模式进入全屏的
                if (getCurrentMode() === 'theater') {
                    wasInFullscreenFromTheater = true;
                    console.log('从影院模式点击全屏按钮,标记状态');
                }

                // 延迟检查是否需要处理退出逻辑
                setTimeout(() => {
                    if (wasInFullscreenFromTheater && getCurrentMode() === 'theater') {
                        console.log('从影院模式进入全屏后退出,自动退出到默认视图');
                        exitTheaterMode();
                        wasInFullscreenFromTheater = false;
                    }
                }, 200);
            }
        }, true);
    }

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

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

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

        // 监听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.tagName === 'YTD-WATCH-FLEXY' ||
                             node.querySelector && node.querySelector('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();
    }
})();

QingJ © 2025

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