YouTube Hide Chat by Default

Hides chat on YouTube live streams by default

目前为 2022-11-02 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube Hide Chat by Default
  3. // @namespace https://skoshy.com
  4. // @version 0.6.1
  5. // @description Hides chat on YouTube live streams by default
  6. // @author Stefan K.
  7. // @match https://*.youtube.com/*
  8. // @grant none
  9. // @icon https://youtube.com/favicon.ico
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. "use strict";
  14. const scriptId = "youtube-hide-chat-by-default";
  15.  
  16. // configurable vars
  17. // - if youtube decides to use a new button type, add it here
  18. const buttonSelectors = ["button"];
  19. // - for different languages for the HIDE CHAT text, add them here
  20. const hideChatTexts = [
  21. 'HIDE CHAT', // english
  22. 'HIDE CHAT REPLAY', // english, chat replay
  23. 'OCULTAR CHAT', // spanish
  24. 'MASQUER LA CONVERSATION PAR CHAT', // french
  25. 'MASQUER LE CLAVARDAGE', // french canada
  26. 'NASCONDI CHAT', // italian
  27. 'OCULTAR CONVERSA', // portuguese
  28. 'চ্চাট লুকুৱাওক', // bengali
  29. 'च्याट लुकाउनुहोस्', // nepali
  30. 'चैट छिपाएं', // hindi
  31. 'チャットを非表示', // japanese
  32. '隐藏聊天记录', // zh-Hans-CN
  33. '隱藏即時通訊', // zh-Hant-TW and zh-Hant-HK
  34. ];
  35.  
  36. const nodeIdString = `${scriptId}-id`;
  37.  
  38. function log(...toLog) {
  39. console.log(`[${scriptId}]:`, ...toLog);
  40. }
  41.  
  42. function getUrlSearchParams() {
  43. return new URLSearchParams(document.location.search.substring(1));
  44. }
  45.  
  46. function isHideChatButton(node) {
  47. return hideChatTexts.includes(node.innerText.toUpperCase().trim());
  48. }
  49.  
  50. function getNodeId(node) {
  51. let nodeId = node.getAttribute(nodeIdString);
  52.  
  53. return nodeId;
  54. }
  55.  
  56. function setAndGetNodeId(node, setIdTo) {
  57. const originalNodeId = getNodeId(node);
  58.  
  59. const nodeId = setIdTo ? setIdTo : Math.random().toString();
  60. node.setAttribute(nodeIdString, nodeId);
  61.  
  62. return { originalNodeId, nodeId };
  63. }
  64.  
  65. function addedNodeHandler(node) {
  66. if (
  67. !node.matches ||
  68. !buttonSelectors.some(b => node.matches(b))
  69. ) {
  70. return;
  71. }
  72.  
  73. if (isHideChatButton(node)) {
  74. log(`Found a hide-chat button`, node);
  75.  
  76. const { nodeId, originalNodeId } = setAndGetNodeId(node, getUrlSearchParams().get('v'));
  77.  
  78. if (originalNodeId === nodeId) return; // we've already automatically triggered hide chat
  79.  
  80. log(`Hid the chat by default`, document.querySelector('yt-live-chat-message-input-renderer'));
  81.  
  82. const tryToHideChatDetails = {
  83. interval: null,
  84. triedAttempts: 0,
  85. };
  86.  
  87. tryToHideChatDetails.interval = setInterval(() => {
  88. tryToHideChatDetails.triedAttempts++;
  89.  
  90. if (document.querySelector('ytd-live-chat-frame iframe')?.offsetHeight ?? 0 > 0) {
  91. log('Clicking again because live chat still there');
  92. node.click();
  93. tryToHideChatDetails.triedAttempts = 0;
  94. }
  95.  
  96. if (tryToHideChatDetails.triedAttempts > 6) {
  97. clearInterval(tryToHideChatDetails.interval);
  98. }
  99. }, 250);
  100.  
  101. return;
  102. }
  103. }
  104.  
  105. const bodyObserver = new MutationObserver(function(mutations) {
  106. mutations.forEach(function(mutation) {
  107. const newNodes = [];
  108.  
  109. mutation.addedNodes.forEach(addedNode => {
  110. newNodes.push(addedNode);
  111.  
  112. // it might be text node or comment node which don't have querySelectorAll
  113. if (addedNode.querySelectorAll) {
  114. buttonSelectors.forEach(bs => {
  115. addedNode.querySelectorAll(bs).forEach((n) => {
  116. newNodes.push(n);
  117. });
  118. });
  119. }
  120. });
  121.  
  122. newNodes.forEach(n => addedNodeHandler(n));
  123. });
  124. });
  125.  
  126. setInterval(() =>
  127. Array.from(
  128. document.querySelectorAll(buttonSelectors.join(', '))
  129. ).forEach(n => addedNodeHandler(n))
  130. , 3000);
  131.  
  132. bodyObserver.observe(document.body, {
  133. attributes: true,
  134. childList: true,
  135. subtree: true,
  136. characterData: true
  137. });
  138. })();