YouTube Live Chat in Fullscreen

Display live chat while watching YouTube videos in fullscreen mode

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         YouTube Live Chat in Fullscreen
// @namespace    https://greasyfork.org/en/users/781396
// @version      1.8
// @description  Display live chat while watching YouTube videos in fullscreen mode
// @author       YAD
// @license      MIT
// @icon         https://www.iconpacks.net/icons/1/free-icon-video-837.png
// @match        *://*.youtube.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Load settings from localStorage or set defaults
    let chatboxWidth = localStorage.getItem('chatboxWidth') || 350;
    let chatboxHeight = localStorage.getItem('chatboxHeight') || 600;
    let chatboxOpacity = localStorage.getItem('chatboxOpacity') || 0.65;
    let chatboxPosX = localStorage.getItem('chatboxPosX') || 420;
    let chatboxPosY = localStorage.getItem('chatboxPosY') || -320;
    let backgroundColor = localStorage.getItem('backgroundColor') || '#000000';
    let backgroundOpacity = localStorage.getItem('backgroundOpacity') || 0.5;

    // CSS styles for transparency
    function getStyles() {
        return `
            yt-live-chat-header-renderer,
            yt-live-chat-message-input-renderer,
            #container.yt-live-chat-restricted-participation-renderer,
            yt-live-chat-renderer {
                background: rgba(${hexToRgb(backgroundColor)}, ${backgroundOpacity}) !important; // Adjust background color
                overflow: hidden;
            }
            .draggable {
                cursor: move;
            }
        `;
    }

    // Convert hex color to RGB
    function hexToRgb(hex) {
        const bigint = parseInt(hex.slice(1), 16);
        const r = (bigint >> 16) & 255;
        const g = (bigint >> 8) & 255;
        const b = bigint & 255;
        return `${r}, ${g}, ${b}`;
    }

    // Function to apply styles to the iframe document
    function applyStylesToIframe(iframeDoc) {
        const styleTag = iframeDoc.createElement('style');
        styleTag.textContent = getStyles();
        iframeDoc.head.appendChild(styleTag);
    }

    // Function to apply styles to the chatbox on load and fullscreen change
    function applyStyles() {
        const iframe = document.querySelector('iframe#chatframe');
        if (iframe) {
            setTimeout(() => {
                applyStylesToIframe(iframe.contentDocument || iframe.contentWindow.document);
            }, 2000); // Adding a 2secs delay to ensure the iframe loads
        }
    }

    // Create settings modal
    const settingsModal = document.createElement('div');
    settingsModal.style.position = 'fixed';
    settingsModal.style.top = '50%';
    settingsModal.style.left = '50%';
    settingsModal.style.transform = 'translate(-50%, -50%)';
    settingsModal.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
    settingsModal.style.color = 'white';
    settingsModal.style.padding = '20px';
    settingsModal.style.borderRadius = '10px';
    settingsModal.style.zIndex = '10001';
    settingsModal.style.display = 'none';

    // Create the title
    const title = document.createElement('h3');
    title.innerText = 'Chatbox Settings';
    settingsModal.appendChild(title);

    // Create width input
    const widthLabel = document.createElement('label');
    widthLabel.innerText = 'Width: ';
    const widthInput = document.createElement('input');
    widthInput.type = 'number';
    widthInput.id = 'chatWidth';
    widthInput.value = chatboxWidth;
    widthInput.style.width = '80px';
    widthLabel.appendChild(widthInput);
    widthLabel.appendChild(document.createTextNode(' px'));
    settingsModal.appendChild(widthLabel);
    settingsModal.appendChild(document.createElement('br'));

    // Create height input
    const heightLabel = document.createElement('label');
    heightLabel.innerText = 'Height: ';
    const heightInput = document.createElement('input');
    heightInput.type = 'number';
    heightInput.id = 'chatHeight';
    heightInput.value = chatboxHeight;
    heightInput.style.width = '80px';
    heightLabel.appendChild(heightInput);
    heightLabel.appendChild(document.createTextNode(' px'));
    settingsModal.appendChild(heightLabel);
    settingsModal.appendChild(document.createElement('br'));

    // Create opacity slider
    const opacityLabel = document.createElement('label');
    opacityLabel.innerText = 'Chat Opacity: ';
    const opacitySlider = document.createElement('input');
    opacitySlider.type = 'range';
    opacitySlider.id = 'chatOpacity';
    opacitySlider.min = '0';
    opacitySlider.max = '1';
    opacitySlider.step = '0.1';
    opacitySlider.value = chatboxOpacity;
    opacityLabel.appendChild(opacitySlider);
    settingsModal.appendChild(opacityLabel);
    settingsModal.appendChild(document.createElement('br'));

    // Create background color input
    const bgColorLabel = document.createElement('label');
    bgColorLabel.innerText = 'Background Color: ';
    const bgColorInput = document.createElement('input');
    bgColorInput.type = 'color';
    bgColorInput.value = backgroundColor;
    bgColorLabel.appendChild(bgColorInput);
    settingsModal.appendChild(bgColorLabel);
    settingsModal.appendChild(document.createElement('br'));

    // Create background opacity slider
    const bgOpacityLabel = document.createElement('label');
    bgOpacityLabel.innerText = 'Background Transparency: ';
    const bgOpacitySlider = document.createElement('input');
    bgOpacitySlider.type = 'range';
    bgOpacitySlider.id = 'bgOpacity';
    bgOpacitySlider.min = '0';
    bgOpacitySlider.max = '1';
    bgOpacitySlider.step = '0.1';
    bgOpacitySlider.value = backgroundOpacity;
    bgOpacityLabel.appendChild(bgOpacitySlider);
    settingsModal.appendChild(bgOpacityLabel);
    settingsModal.appendChild(document.createElement('br'));

    // Create X position slider
    const posXLabel = document.createElement('label');
    posXLabel.innerText = 'X Position: ';
    const posXSlider = document.createElement('input');
    posXSlider.type = 'range';
    posXSlider.id = 'chatPosX';
    posXSlider.min = `-${window.innerWidth}`;
    posXSlider.max = `${window.innerWidth}`;
    posXSlider.value = chatboxPosX;
    posXLabel.appendChild(posXSlider);
    settingsModal.appendChild(posXLabel);
    settingsModal.appendChild(document.createElement('br'));

    // Create Y position slider
    const posYLabel = document.createElement('label');
    posYLabel.innerText = 'Y Position: ';
    const posYSlider = document.createElement('input');
    posYSlider.type = 'range';
    posYSlider.id = 'chatPosY';
    posYSlider.min = `-${window.innerHeight}`;
    posYSlider.max = `${window.innerHeight}`;
    posYSlider.value = chatboxPosY;
    posYLabel.appendChild(posYSlider);
    settingsModal.appendChild(posYLabel);
    settingsModal.appendChild(document.createElement('br'));

    // Create apply button
    const applyButton = document.createElement('button');
    applyButton.innerText = 'Apply';
    applyButton.id = 'applySettings';
    settingsModal.appendChild(applyButton);

    // Create reset button
    const resetButton = document.createElement('button');
    resetButton.innerText = 'Reset';
    resetButton.id = 'resetSettings';
    settingsModal.appendChild(resetButton);

    document.body.appendChild(settingsModal);

    // Handle settings button click (open modal)
    const settingsButton = document.createElement('button');
    settingsButton.innerText = '⚙';
    settingsButton.style.position = 'fixed';
    settingsButton.style.top = '2%';
    settingsButton.style.right = '2%';
    settingsButton.style.zIndex = '10000';
    settingsButton.style.display = 'none';
    settingsButton.addEventListener('click', () => {
        settingsModal.style.display = 'block';
    });

    document.body.appendChild(settingsButton);

    // Update iframe settings in real-time
    function updateChatbox() {
        const iframe = document.querySelector('iframe#chatframe');
        if (iframe) {
            iframe.style.width = `${chatboxWidth}px`;
            iframe.style.height = `${chatboxHeight}px`;
            iframe.style.position = 'fixed';
            iframe.style.left = `${(window.innerWidth / 2) + parseInt(chatboxPosX)}px`;
            iframe.style.top = `${(window.innerHeight / 2) + parseInt(chatboxPosY)}px`;
            iframe.style.opacity = chatboxOpacity;
            iframe.style.borderRadius = '12px';
            applyStyles();
        }
    }

    // Handle apply button click
    applyButton.addEventListener('click', () => {
        // Update settings
        chatboxWidth = widthInput.value;
        chatboxHeight = heightInput.value;
        chatboxOpacity = opacitySlider.value;
        backgroundColor = bgColorInput.value;
        backgroundOpacity = bgOpacitySlider.value;
        chatboxPosX = posXSlider.value;
        chatboxPosY = posYSlider.value;

        // Save settings to localStorage
        localStorage.setItem('chatboxWidth', chatboxWidth);
        localStorage.setItem('chatboxHeight', chatboxHeight);
        localStorage.setItem('chatboxOpacity', chatboxOpacity);
        localStorage.setItem('backgroundColor', backgroundColor);
        localStorage.setItem('backgroundOpacity', backgroundOpacity);
        localStorage.setItem('chatboxPosX', chatboxPosX);
        localStorage.setItem('chatboxPosY', chatboxPosY);

        settingsModal.style.display = 'none';
        updateChatbox();
    });

    // Handle reset settings
    resetButton.addEventListener('click', () => {
        localStorage.removeItem('chatboxWidth');
        localStorage.removeItem('chatboxHeight');
        localStorage.removeItem('chatboxOpacity');
        localStorage.removeItem('backgroundColor');
        localStorage.removeItem('backgroundOpacity');
        localStorage.removeItem('chatboxPosX');
        localStorage.removeItem('chatboxPosY');

        // Reset to defaults
        chatboxWidth = 350;
        chatboxHeight = 600;
        chatboxOpacity = 0.65;
        backgroundColor = '#000000';
        backgroundOpacity = 0.5;
        chatboxPosX = 420;
        chatboxPosY = -320;

        // Update UI elements
        widthInput.value = chatboxWidth;
        heightInput.value = chatboxHeight;
        opacitySlider.value = chatboxOpacity;
        bgColorInput.value = backgroundColor;
        bgOpacitySlider.value = backgroundOpacity;
        posXSlider.value = chatboxPosX;
        posYSlider.value = chatboxPosY;

        updateChatbox(); // Apply reset settings
    });

    // Handle slider inputs for real-time updates
    opacitySlider.addEventListener('input', () => {
        chatboxOpacity = opacitySlider.value;
        updateChatbox();
    });

    bgOpacitySlider.addEventListener('input', () => {
        backgroundOpacity = bgOpacitySlider.value;
        updateChatbox();
    });

    bgColorInput.addEventListener('input', () => {
        backgroundColor = bgColorInput.value;
        updateChatbox();
    });

    widthInput.addEventListener('input', () => {
        chatboxWidth = widthInput.value;
        updateChatbox();
    });

    heightInput.addEventListener('input', () => {
        chatboxHeight = heightInput.value;
        updateChatbox();
    });

    posXSlider.addEventListener('input', () => {
        chatboxPosX = posXSlider.value;
        updateChatbox();
    });

    posYSlider.addEventListener('input', () => {
        chatboxPosY = posYSlider.value;
        updateChatbox();
    });

    // Function to adjust slider value with scroll wheel
    function adjustSliderValue(slider, delta) {
        const step = parseFloat(slider.step) || 10;
        const min = parseFloat(slider.min) || 0;
        const max = parseFloat(slider.max) || 100;
        let value = parseFloat(slider.value) || 0;

        value += delta * step;
        if (value < min) value = min;
        if (value > max) value = max;

        slider.value = value;
        slider.dispatchEvent(new Event('input'));
    }

    // Handle mouse scroll on sliders
    document.querySelectorAll('input[type="range"]').forEach(slider => {
        slider.addEventListener('wheel', event => {
            event.preventDefault();
            const delta = Math.sign(event.deltaY) * -1;
            adjustSliderValue(slider, delta);
        });

        // Handle arrow key controls on sliders
        slider.addEventListener('keydown', event => {
            if (event.key === 'ArrowUp' && slider.id === 'chatPosY') {
                adjustSliderValue(slider, -1);
            } else if (event.key === 'ArrowDown' && slider.id === 'chatPosY') {
                adjustSliderValue(slider, 1);
            } else if (event.key === 'ArrowLeft' && slider.id === 'chatPosX') {
                adjustSliderValue(slider, -1);
            } else if (event.key === 'ArrowRight' && slider.id === 'chatPosX') {
                adjustSliderValue(slider, 1);
            } else if ((event.key === 'ArrowUp' || event.key === 'ArrowRight') && slider.id !== 'chatPosX' && slider.id !== 'chatPosY') {
                adjustSliderValue(slider, 1);
            } else if ((event.key === 'ArrowDown' || event.key === 'ArrowLeft') && slider.id !== 'chatPosX' && slider.id !== 'chatPosY') {
                adjustSliderValue(slider, -1);
            }
        });
    });

    // Handle fullscreen changes
    function handleFullscreenChange() {
        const iframe = document.querySelector('iframe#chatframe');
        if (!iframe) return;

        const isFullscreen = document.fullscreenElement;

        if (isFullscreen) {
            updateChatbox();
            applyStyles();
            settingsButton.style.display = 'block';
        } else {
            settingsButton.style.display = 'none';
            settingsModal.style.display = 'none';
            // Reset to default values (do not remove saved settings)
            iframe.style.width = '';
            iframe.style.height = '';
            iframe.style.position = '';
            iframe.style.left = '';
            iframe.style.top = '';
            iframe.style.opacity = '';
        }
    }

    // Listen for fullscreen changes
    document.addEventListener('fullscreenchange', handleFullscreenChange);

    // Initial chatbox update
    updateChatbox();
    applyStylesOnLoad();
})();