// ==UserScript==
// @name Live Chat on YouTube Mobile
// @version 1.3.1
// @description Integrates the live chat into the mobile version of YouTube
// @match https://m.youtube.com/*
// @grant none
// @namespace https://greasyfork.org/en/scripts/519614-live-chat-on-youtube-mobile
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// CONFIGURATION
const AUTO_HIDE_CHAT_BUTTON = true; // If true, the button only appears when a Live/Premiere is detected
const AUTO_SHOW_CHAT = false; // If true, the chat automatically opens when a Live/Premiere loads.
let currentVideoId = null, isIframeLoaded = false, observer = null, previousWidth = window.innerWidth, isChatVisible = false;
const elements = {};
const isVideoPage = () => window.location.pathname === '/watch' && window.location.search.includes('v=');
const getVideoId = () => (window.location.search.match(/v=([^&]+)/) || [])[1];
const isPortraitOrientation = () => window.innerHeight > window.innerWidth;
const isLiveOrPremiere = () => {
if (!AUTO_HIDE_CHAT_BUTTON) {
return true;
}
const infoContainer = elements.infoContainer;
if (infoContainer) {
const content = infoContainer.textContent || '';
return content.toLowerCase().includes('watching now')
|| content.toLowerCase().includes('personas mirando ahora')
|| content.toLowerCase().includes('usuarios viéndolo ahora')
|| content.toLowerCase().includes('viewing now');
}
return false;
};
const showLoader = (darkbackground = '#111') => {
elements.loader.style.display = 'block';
elements.chatIframe.style.opacity = '0';
elements.chatContainer.style.backgroundColor = darkbackground;
};
const hideLoader = () => {
elements.loader.style.display = 'none';
elements.chatIframe.style.opacity = '1';
elements.chatContainer.style.backgroundColor = '#333';
};
const updateChatPosition = () => {
const videoPlayer = elements.videoPlayer;
if (videoPlayer) {
const videoRect = videoPlayer.getBoundingClientRect();
const isVerticalVideo = videoRect.height > videoRect.width && videoRect.height > 100;
elements.chatContainer.style.top = isVerticalVideo ? '50%' : `${videoRect.bottom}px`;
elements.chatContainer.style.height = isVerticalVideo ? '50%' : 'auto';
elements.chatContainer.style.bottom = '0';
} else {
elements.chatContainer.style.top = '250px';
elements.chatContainer.style.height = 'auto';
}
};
const setButtonVisibility = (visible) => {
elements.button.style.display = (visible && isPortraitOrientation() && isLiveOrPremiere()) ? 'block' : 'none';
};
const closeChat = () => {
if (elements.chatContainer.style.display !== 'none') {
elements.chatContainer.style.display = 'none';
elements.button.innerText = '💬';
document.body.classList.remove('chat-open-no-scroll');
isChatVisible = false;
}
};
const showChat = () => {
if (!isIframeLoaded) {
showLoader();
} else {
hideLoader();
}
updateChatPosition();
elements.chatContainer.style.display = 'block';
document.body.classList.add('chat-open-no-scroll');
elements.button.innerText = '✖️';
elements.chatIframe.focus();
isChatVisible = true;
};
const toggleChat = () => {
if (elements.button.style.display === 'none') return;
if (!isChatVisible) {
showChat();
} else {
closeChat();
}
};
const preloadChatIframe = () => {
const videoId = getVideoId();
if (isVideoPage() && videoId && isLiveOrPremiere() && videoId !== currentVideoId) {
currentVideoId = videoId;
isIframeLoaded = false;
elements.chatIframe.style.opacity = '0';
const newChatSrc = `https://www.youtube.com/live_chat?v=${videoId}&embed_domain=${window.location.hostname}`;
elements.chatIframe.src = newChatSrc;
} else if (videoId === currentVideoId && isIframeLoaded) {
setButtonVisibility(true);
}
};
const updateDOMElementsCache = () => {
elements.videoPlayer = document.querySelector('video');
elements.infoContainer = document.querySelector('.slim-video-information-content');
};
const handlePageAndVideoState = () => {
const onVideoPage = isVideoPage();
const newVideoId = getVideoId();
if (onVideoPage && newVideoId !== currentVideoId) {
updateDOMElementsCache();
preloadChatIframe();
if (AUTO_SHOW_CHAT && isPortraitOrientation() && isLiveOrPremiere()) {
showChat();
}
} else if (!onVideoPage) {
elements.videoPlayer = null;
elements.infoContainer = null;
currentVideoId = null;
isIframeLoaded = false;
elements.chatIframe.src = '';
}
setButtonVisibility(onVideoPage);
if (!onVideoPage) {
closeChat();
} else if (isChatVisible) {
if (newVideoId && newVideoId !== currentVideoId) {
closeChat();
} else if (!isPortraitOrientation() || !isLiveOrPremiere()) {
closeChat();
} else {
updateChatPosition();
}
}
};
const createUIElements = () => {
elements.button = document.createElement('button');
Object.assign(elements.button.style, {
position: 'fixed', bottom: '64px', right: '20px', zIndex: '9999', padding: '10px',
backgroundColor: '#ff0000', color: '#fff', border: 'none', borderRadius: '50%',
width: '45px', height: '45px', fontSize: '20px', lineHeight: '0',
boxShadow: '0 3px 6px rgba(0,0,0,0.3)', cursor: 'pointer', display: 'none'
});
elements.button.innerText = '💬';
document.body.appendChild(elements.button);
elements.chatContainer = document.createElement('div');
Object.assign(elements.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'
});
document.body.appendChild(elements.chatContainer);
elements.chatIframe = document.createElement('iframe');
Object.assign(elements.chatIframe.style, {
width: '100%', height: '100%', border: 'none', maxWidth: '100%', opacity: '0'
});
elements.chatContainer.appendChild(elements.chatIframe);
elements.loader = document.createElement('div');
Object.assign(elements.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'
});
elements.chatContainer.appendChild(elements.loader);
const styleSheet = document.createElement("style");
styleSheet.type = "text/css";
styleSheet.innerText = `@keyframes spin { 0% { transform: translate(-50%, -50%) rotate(0deg); } 100% { transform: translate(-50%, -50%) rotate(360deg); } } .chat-open-no-scroll { overflow: hidden !important; position: fixed !important; width: 100% !important; height: 100% !important; }`;
document.head.appendChild(styleSheet);
};
const initializeObserver = () => {
const targetNode = document.querySelector('body');
if (targetNode) {
observer = new MutationObserver(() => setTimeout(handlePageAndVideoState, 100));
observer.observe(targetNode, { childList: true, subtree: true });
}
window.addEventListener('popstate', handlePageAndVideoState);
window.addEventListener('resize', () => {
const isRotationDetected = window.innerWidth !== previousWidth;
if (isRotationDetected) handlePageAndVideoState();
else if (isChatVisible) updateChatPosition();
previousWidth = window.innerWidth;
});
handlePageAndVideoState();
};
const initialize = () => {
if (window.self !== window.top) return;
createUIElements();
updateDOMElementsCache();
elements.button.addEventListener('click', toggleChat);
elements.chatIframe.onload = () => {
isIframeLoaded = true;
if (isChatVisible) {
hideLoader();
}
setButtonVisibility(true);
};
preloadChatIframe();
initializeObserver();
};
initialize();
})();