Live Chat on YouTube Mobile (Auto Load + Auto Recreate)

Auto-loads YouTube Live Chat on mobile.

// ==UserScript==
// @name         Live Chat on YouTube Mobile (Auto Load + Auto Recreate)
// @version      1.4
// @description  Auto-loads YouTube Live Chat on mobile.
// @match        https://m.youtube.com/*
// @grant        none
// @license      MIT
// @namespace https://greasyfork.org/users/1360319
// ==/UserScript==

(function() {
    'use strict';

    // --- Chat Container ---
    const chatContainer = document.createElement('div');
    Object.assign(chatContainer.style, {
        display: 'none',
        position: 'fixed',
        width: '100%',
        top: '250px',
        bottom: '0',
        height:'auto',
        left: '0',
        right: '0',
        borderTop: '1px solid #555',
        backgroundColor: '#333',
        zIndex: '9998',
        overflow: 'hidden'
    });

    // --- Loader Spinner ---
    const loader = document.createElement('div');
    Object.assign(loader.style, {
        border: '4px solid #555',
        borderTop: '4px solid #ff0000',
        borderRadius: '50%',
        width: '30px',
        height: '30px',
        animation: 'spin 1s linear infinite',
        position: 'absolute',
        top: '50%',
        left: '50%',
        transform: 'translate(-50%, -50%)',
        zIndex: '10000',
        display: 'none'
    });

    const styleSheet = document.createElement("style");
    styleSheet.textContent = `
        @keyframes spin {
            0% { transform: translate(-50%, -50%) rotate(0deg); }
            100% { transform: translate(-50%, -50%) rotate(360deg); }
        }
        .YtmBottomSheetOverlayRendererOverlayContainer{opacity:0!important}
        bottom-sheet-container{
        z-index:55000!important;
        }
        #ytm-chat-top-toggle {
            position: absolute;
            top: 10px;
            left: 50%;
            background:#121212;
            transform: translateX(-50%);
            background: transparent;
            color: grey;
            border: none;
            border-radius: 6px;
            padding: 6px 12px;
            font-size: 14px;
            cursor: pointer;
            z-index: 10001;
            touch-action: manipulation;
        }
    `;
    document.head.appendChild(styleSheet);

    // --- Add top-center toggle button ---
    const topToggleBtn = document.createElement('button');
    topToggleBtn.id = 'ytm-chat-top-toggle';
    topToggleBtn.textContent = 'Hide Chat';
    chatContainer.appendChild(topToggleBtn);
    chatContainer.appendChild(loader);
    document.body.appendChild(chatContainer);

    // --- State ---
    let currentVideoId,chatButton,lastVideoId,chatIframe,viewerInterval,buttonCheckInterval,streamTimeSpan,actualStartTime,streamTimeInterval,ti= null;
    let ii=false;
    const showLoader = () => {
        loader.style.display = 'block';
        chatContainer.style.backgroundColor = '#111';
    };
    const hideLoader = () => {
        loader.style.display = 'none';
        chatContainer.style.backgroundColor = '#333';
    };

    // --- Create and insert "Currently Watching" and "Hide Chat" buttons ---
const ensureChatButtons = () => {
    const controlsTop = document.querySelector('.player-controls-top');
    if (controlsTop) {
   chatButton = document.getElementById('ytm-hide-chat-btn');
    // Create Hide/Display Chat button
    if (!chatButton) {
        chatButton = document.createElement('button');
        chatButton.id = 'ytm-hide-chat-btn';
        chatButton.textContent = '💬';
        chatButton.style.zIndex = '5555';
        Object.assign(chatButton.style, {
            color: '#fff',
            border: 'none',
            opacity:'0.7',
            display:'none',
            borderRadius: '4px',
            padding: '4px 6px',
            marginRight: '6px',
            touchAction: 'manipulation',
            cursor: 'pointer',
            fontSize: '20px'
        });
    }
        if (!document.getElementById('ytm-hide-chat-btn')) {
            controlsTop.insertBefore(chatButton, controlsTop.firstChild);
        chatButton.addEventListener('click', toggleChatVisibility, { passive: true });
chatButton.addEventListener('touchstart', toggleChatVisibility, { passive: true });
topToggleBtn.addEventListener('click', toggleChatVisibility, { passive: true });
topToggleBtn.addEventListener('touchstart', toggleChatVisibility, { passive: true });
        }
    }
};

    // --- Unified toggle function ---
    const toggleChatVisibility = () => {
        if(!ii){
            ii=true;
         chatButton = document.getElementById('ytm-hide-chat-btn');
        const hidden = chatContainer.style.visibility === 'hidden';
        if (hidden) {
            chatContainer.style.visibility = 'visible';
            if (chatButton) {
            chatButton.style.display='none';
            }
        } else {
            //avoid accidental touches/clicks
const blocker = document.createElement('div');
blocker.style.position = 'fixed';
blocker.style.top = 0;
blocker.style.left = 0;
blocker.style.width = '100%';
blocker.style.height = '100%';
blocker.style.background = 'transparent';
blocker.style.zIndex = 999999;
blocker.style.pointerEvents = 'auto';
document.body.appendChild(blocker);
setTimeout(() => blocker.remove(), 200);
            chatContainer.style.visibility = 'hidden';
            if (chatButton) 
            {
            chatButton.style.display='';
            }
        }
        clearTimeout(ti);
      ti= setTimeout(() => ii=false, 200);
        }
    };


    // --- Create chat iframe ---
const createChatIframe = async (videoId) => {
        if (chatIframe && chatIframe.parentElement) chatIframe.remove();
        chatIframe = document.createElement('iframe');
        Object.assign(chatIframe.style, {
            width: '100%',
            height: '100%',
            border: 'none',
            maxWidth: '100%',
            opacity: '0'
        });

        chatIframe.src = `https://www.youtube.com/live_chat?v=${videoId}&embed_domain=${window.location.hostname}`;
        chatIframe.onload = () => {
            hideLoader();
            chatIframe.style.opacity = '1';
        };
if(chatButton) chatButton.style.display='none';
        chatContainer.appendChild(chatIframe);
        currentVideoId = videoId;
        // Clear old interval if any
     [ buttonCheckInterval, streamTimeInterval].forEach(i => { if (i) clearInterval(i); });
 buttonCheckInterval = streamTimeInterval = null;
        ensureChatButtons();
        if (!buttonCheckInterval) {
            buttonCheckInterval = setInterval(ensureChatButtons, 1000);
        }
    };

    // --- Close chat and cleanup ---
    const closeChat = () => {
         lastVideoId = null;
    [buttonCheckInterval, streamTimeInterval].forEach(i => clearInterval(i));
    viewerInterval = buttonCheckInterval = streamTimeInterval = null;
        if (chatContainer.style.display !== 'none') {
            if (chatIframe && chatIframe.parentElement) chatIframe.remove();
            chatIframe = null;
            chatContainer.style.display = 'none';
            hideLoader();
        }
    if (streamTimeSpan && streamTimeSpan.parentElement) {
        streamTimeSpan.remove();
        streamTimeSpan = null;
    }
    };

    // --- Check if current page is a live video ---
    const isVideoPage = () => {
    return (
        window.location.pathname === '/watch' &&
        window.location.search.includes('v=') &&
        (document.querySelector('.ytwPlayerTimeDisplayLiveDot.ytwPlayerTimeDisplayPill > div > span > .yt-core-attributed-string')?.textContent.includes('live') ||
         document.querySelector('.secondary-text > .yt-core-attributed-string')?.textContent.includes('watching now') ||
         document.querySelector('.secondary-text > .yt-core-attributed-string')?.textContent.includes('en direct') ||
         document.querySelector('.secondary-text > .yt-core-attributed-string')?.textContent.includes('en directo') ||
         document.querySelector('.secondary-text > .yt-core-attributed-string')?.textContent.includes('en vivo') ||
         document.querySelector('.secondary-text > .yt-core-attributed-string')?.textContent.includes('em direto'))
    );
};

    // --- Auto-load chat ---
    const autoLoadChat = () => {
        if (isVideoPage() && document.querySelector('#player') && document.querySelector('video')) {
           if(document.querySelector('#player').getBoundingClientRect().bottom>100)    chatContainer.style.top = `${document.querySelector('#player').getBoundingClientRect().bottom}px`;
            const videoIdMatch = window.location.search.match(/v=([^&]+)/);
            if (!videoIdMatch) return;
            const videoId = videoIdMatch[1];
            showLoader();
            createChatIframe(videoId);
            chatContainer.style.display = 'block';
            chatContainer.style.visibility = 'visible';
        } else {
            closeChat();
        }
    };

    // --- Monitor video changes ---
    setInterval(() => {
        // remove "ready to shop" banner
        document.querySelector('.YtmBottomSheetOverlayRendererHeader')?.children[1]?.children[0]?.click();
if (document.body.hasAttribute('bottom-sheet-open')) {
  document.body.removeAttribute('bottom-sheet-open');
}

        const videoIdMatch = window.location.search.match(/v=([^&]+)/);
        const newVideoId = videoIdMatch ? videoIdMatch[1] : null;

        if (isVideoPage()) {
            if (chatContainer.style.display === 'none' || newVideoId !== currentVideoId) {
                autoLoadChat();
            }

            const videoPlayer = document.querySelector('#player');
            if (videoPlayer) {
                const rect = videoPlayer.getBoundingClientRect();
               if(rect.bottom>100) chatContainer.style.top = `${rect.bottom}px`;
     }
    } else {
            closeChat();
            currentVideoId = null;
        }
    }, 1000);

    autoLoadChat();
})();