您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Hide live chat by default on live streams
// ==UserScript== // @name YouTube - Hide Live Chat By Default // @namespace https://gist.github.com/lbmaian/94824cef728917a53d3c6e6ea885469c // @version 0.14 // @description Hide live chat by default on live streams // @author lbmaian // @match https://www.youtube.com/* // @exclude https://www.youtube.com/embed/* // @icon https://www.youtube.com/favicon.ico // @run-at document-start // @grant none // ==/UserScript== (function() { 'use strict'; const DEBUG = false; const logContext = '[YouTube - Hide Live Chat]'; var debug; if (DEBUG) { debug = function(...args) { console.debug(logContext, ...args); } } else { debug = function(...args) {} } function log(...args) { console.log(logContext, ...args); } function warn(...args) { console.warn(logContext, ...args); } function error(...args) { console.error(logContext, ...args); } // Note: Following all relies on YT internals. function updateChatData(data, collapsed) { if (DEBUG) { debug('data (before)', window.structuredClone(data)); } const liveChatRenderer = data.liveChatRenderer; if (liveChatRenderer) { // if no live chat despite #chat existing, e.g. "Live chat replay is not available for this video." const expandedByDefault = liveChatRenderer.initialDisplayState === 'LIVE_CHAT_DISPLAY_STATE_EXPANDED'; if (expandedByDefault && collapsed) { if (collapsed) { log('hiding live chat'); } debug('data.liveChatRenderer.initialDisplayState:', liveChatRenderer.initialDisplayState, '=>', 'LIVE_CHAT_DISPLAY_STATE_COLLAPSED'); liveChatRenderer.initialDisplayState = 'LIVE_CHAT_DISPLAY_STATE_COLLAPSED'; } const toggleButtonRenderer = liveChatRenderer.showHideButton?.toggleButtonRenderer; if (toggleButtonRenderer) { if (expandedByDefault) { debug('data.liveChatRenderer.showHideButton.toggleButtonRenderer.defaultText/toggledText swapped'); [toggleButtonRenderer.defaultText, toggleButtonRenderer.toggledText] = [toggleButtonRenderer.toggledText, toggleButtonRenderer.defaultText]; } const isToggled = !collapsed; if (DEBUG && toggleButtonRenderer.isToggled !== isToggled) { debug('data.liveChatRenderer.showHideButton.toggleButtonRenderer.isToggled', toggleButtonRenderer.isToggled, '=>', isToggled); } toggleButtonRenderer.isToggled = isToggled; } if (DEBUG) { debug('data (updated)', window.structuredClone(data)); } return expandedByDefault; } else { return false; } } // Navigating to YouTube watch page can happen via AJAX rather than new page load. // We can monitor this with YT's custom yt-page-data-fetched event, // which conveniently also fires even for new/refreshed pages. // yt-navigate-finish would also work (evt.detail.detail) but yt-page-data-fetched fires earlier. document.addEventListener('yt-page-data-fetched', evt => { debug('Navigated to', evt.detail.pageData.url); debug(evt); const conversationBar = evt.detail.pageData.response?.contents?.twoColumnWatchNextResults?.conversationBar; debug('yt-page-data-fetched pageData.response contents.twoColumnWatchNextResults.conversationBar (corresponds to #chat.data)', conversationBar); // If response doesn't include conversationBar, there won't be a #chat element at all. if (conversationBar) { // If #chat element isn't created yet, default collapsed to true. // Else keep current collapsed status between pages. // TODO: sometimes for new pages when chat doesn't exist yet, this apparently happens too late? // (chat already initialized with old data) and chat thus remains open? // Detect & fix this - use chat.parentComponent (ytd-watch-flexy)'s updatePageData_ or ytd-app's onYtPageDataFetched? const chat = document.getElementById('chat'); let collapsed; if (chat) { collapsed = chat.collapsed; log('existing #chat', chat, 'collapsed:', collapsed); } else { log('no existing #chat, defaulting collapsed: true'); collapsed = true; } updateChatData(conversationBar, collapsed); } }); })();