Greasy Fork

Greasy Fork is available in English.

YouTube网页全屏

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

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

// ==UserScript==
// @name              YouTube FullWindow
// @name:zh-CN        YouTube网页全屏
// @name:ja           YouTubeフルウィンドウ
// @namespace         http://tampermonkey.net/
// @version           1.1
// @description       Expands YouTube theater mode 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;

    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;
            }

            /* 隐藏页面右侧滚动条 */
            .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]) {
                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 handleKeyDown(event) {
        // 检查是否在输入框或文本区域中
        const activeElement = document.activeElement;
        const isTextInput = activeElement.tagName === 'INPUT' ||
              activeElement.tagName === 'TEXTAREA' ||
              activeElement.isContentEditable;

        if (isTextInput) {
            return;
        }

        // 使用新的状态判断方式
        const currentMode = getCurrentMode();

        // 只在影院模式下响应ESC键
        if (currentMode === 'theater' && event.keyCode === 27) { // ESC键
            event.preventDefault();
            event.stopPropagation();

            // 点击影院模式按钮退出
            const theaterButton = document.querySelector('.ytp-size-button.ytp-button');
            if (theaterButton) {
                theaterButton.click();
            }
        }
    }

    function observeTheaterModeChanges() {
        // 如果已有观察器,先断开
        if (theaterModeObserver) {
            theaterModeObserver.disconnect();
        }

        // 查找ytd-watch-flexy元素
        const watchFlexy = document.querySelector('ytd-watch-flexy');
        if (!watchFlexy) {
            // 如果元素不存在,稍后重试
            setTimeout(observeTheaterModeChanges, 1000);
            return;
        }

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

                    // 只有在影院模式时启用网页全屏样式
                    toggleFullscreenState(currentMode === 'theater');
                }
            });
        });

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

        // 初始状态检查
        const currentMode = getCurrentMode();
        console.log('初始播放器模式:', currentMode);
        toggleFullscreenState(currentMode === 'theater');
    }

    function handlePageChange() {
        console.log('检测到页面变化,重新初始化...');

        // 确保退出全屏状态
        toggleFullscreenState(false);

        // 重新添加样式
        addFullscreenStyles();

        // 重新观察影院模式变化
        setTimeout(observeTheaterModeChanges, 500);
    }

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

        addFullscreenStyles();

        // 添加键盘事件监听
        document.addEventListener('keydown', handleKeyDown, true);

        // 监听影院模式状态变化
        observeTheaterModeChanges();

        // 监听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) { // 元素节点
                            if (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变化(YouTube是单页应用)
    let lastUrl = location.href;
    const urlObserver = new MutationObserver(() => {
        const url = location.href;
        if (url !== lastUrl) {
            lastUrl = url;
            console.log('URL变化:', url);
            handlePageChange();
        }
    });

    urlObserver.observe(document, {
        subtree: true,
        childList: true
    });

    // 等待页面加载完成后初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();