Youtube 隐藏工具

快捷隐藏 YouTube 评论区、相关推荐、视频结尾推荐和设置菜单

当前为 2023-08-25 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Youtube Hide Tool
  3. // @name:zh-TW Youtube 隱藏工具
  4. // @name:zh-CN Youtube 隐藏工具
  5. // @name:ja Youtube 非表示ツール
  6. // @name:ko 유튜브 숨기기 도구
  7. // @name:en Youtube Hide Tool
  8. // @name:de Youtube Versteckwerkzeug
  9. // @name:pt Ferramenta de Ocultação do Youtube
  10. // @name:es Herramienta de Ocultación de Youtube
  11. // @name:fr Outil de Masquage de Youtube
  12. // @name:hi यूट्यूब छुपाने का उपकरण
  13. // @name:id Alat Sembunyikan Youtube
  14. // @version 0.0.18
  15. // @author HentaiSaru
  16. // @description 快捷隱藏 YouTube 留言區、相關推薦、影片結尾推薦和設置選單
  17. // @description:zh-TW 快捷隱藏 YouTube 留言區、相關推薦、影片結尾推薦和設置選單
  18. // @description:zh-CN 快捷隐藏 YouTube 评论区、相关推荐、视频结尾推荐和设置菜单
  19. // @description:ja YouTubeのコメント欄、関連おすすめ、動画の最後のおすすめ、設定メニューを素早く非表示にする
  20. // @description:ko 빠른 YouTube 댓글 영역, 관련 추천, 비디오 끝 추천 및 설정 메뉴 숨기기
  21. // @description:en Quickly hide YouTube comments, related recommendations, video end recommendations, and settings menu
  22. // @description:de Schnell verstecken YouTube Kommentare, verwandte Empfehlungen, Video-Ende-Empfehlungen und Einstellungsmenü
  23. // @description:pt Ocultar rapidamente comentários do YouTube, recomendações relacionadas, recomendações de final de vídeo e menu de configurações
  24. // @description:es Ocultar rápidamente comentarios de YouTube, recomendaciones relacionadas, recomendaciones de final de video y menú de configuración
  25. // @description:fr Masquer rapidement les commentaires de YouTube, les recommandations connexes, les recommandations de fin de vidéo et le menu des paramètres
  26. // @description:hi यूट्यूब टिप्पणियाँ, संबंधित सिफारिशें, वीडियो के अंत की सिफारिशें और सेटिंग्स मेनू को त्वरित रूप से छुपाएं
  27. // @description:id Sembunyikan cepat komentar YouTube, rekomendasi terkait, rekomendasi akhir video, dan menu pengaturan
  28.  
  29. // @match *://www.youtube.com/*
  30. // @icon https://cdn-icons-png.flaticon.com/512/1383/1383260.png
  31.  
  32. // @license MIT
  33. // @namespace https://greasyfork.org/users/989635
  34.  
  35. // @run-at document-end
  36. // @grant GM_setValue
  37. // @grant GM_getValue
  38. // @grant GM_addStyle
  39. // @grant GM_registerMenuCommand
  40. // ==/UserScript==
  41.  
  42. (function() {
  43. const pattern = /^https:\/\/www\.youtube\.com\/.+$/;
  44. var currentUrl;
  45. const observer = new MutationObserver(() => {
  46. currentUrl = window.location.href;
  47. if (pattern.test(currentUrl) && !document.body.hasAttribute("data-hide")) {
  48. document.body.setAttribute("data-hide", true);
  49. let transform = false, set; RunMaim();
  50. async function RunMaim() {
  51. GM_addStyle(`
  52. .ytp-ce-element{opacity: 0.1!important;}
  53. .ytp-ce-element:hover{opacity: 1!important;}
  54. `);
  55. const VVP_Pattern = /^https:\/\/www\.youtube\.com\/watch\?v=.+$/, // 判斷在播放頁面運行
  56. Playlist_Pattern = /^https:\/\/www\.youtube\.com\/playlist\?list=.+$/, // 判斷在播放清單運行
  57. language = display_language(navigator.language),
  58. Lookup_Delay = 300;
  59. /* 菜單 */
  60. GM_registerMenuCommand(language[0], function() {alert(language[1])});
  61.  
  62. /* 設置觸發 */
  63. async function SetTrigger(element) {
  64. element.style.display = "none";
  65. return new Promise(resolve => {
  66. if (element.style.display === "none") {resolve(true)}
  67. else {resolve(false)}
  68. });
  69. }
  70. /* 獲取設置 */
  71. if (VVP_Pattern.test(currentUrl)) {
  72. set = GM_getValue("Minimalist", null);
  73. if (set && set !== null) {
  74. let interval;
  75. interval = setInterval(() => {
  76. const [end, below, secondary, related] = ["end", "below", "secondary", "related"].map(selector => document.getElementById(selector));
  77. if (end && below && secondary && related) {
  78. Promise.all([SetTrigger(end), SetTrigger(below), SetTrigger(secondary), SetTrigger(related)]).then(results => {
  79. if (results[0] && results[1] && results[2] && results[3]) {
  80. clearInterval(interval); return;
  81. }
  82. });
  83. }
  84. }, Lookup_Delay);
  85. }
  86. set = GM_getValue("Trigger_1", null);
  87. if (set && set !== null){
  88. let interval;
  89. interval = setInterval(() => {
  90. const [chat, secondary, related] = ["chat-container", "secondary", "related"].map(selector => document.getElementById(selector));
  91. if (chat && secondary && related) {
  92. Promise.all([SetTrigger(chat), SetTrigger(secondary), SetTrigger(related)]).then(results => {
  93. if (results[0] && results[1] && results[2]) {clearInterval(interval)}
  94. });
  95. }
  96. }, Lookup_Delay);
  97. }
  98. set = GM_getValue("Trigger_2", null);
  99. if (set && set !== null){
  100. let interval;
  101. interval = setInterval(() => {
  102. let comments = document.getElementById("comments");
  103. if (comments) {
  104. SetTrigger(comments).then(() => {clearInterval(interval)});
  105. }
  106. }, Lookup_Delay);
  107. }
  108. set = GM_getValue("Trigger_3", null);
  109. if (set && set !== null){
  110. let interval;
  111. interval = setInterval(() => {
  112. const [menu, actions] = ["menu-container", "actions"].map(selector => document.getElementById(selector));
  113. if (menu && actions) {
  114. Promise.all([SetTrigger(menu), SetTrigger(actions)]).then(results => {
  115. if (results[0] && results[1]) {clearInterval(interval)}
  116. });
  117. }
  118. }, Lookup_Delay);
  119. }
  120. } else if (Playlist_Pattern.test(currentUrl)) {
  121. set = GM_getValue("Trigger_4", null);
  122. if (set && set !== null){
  123. let interval;
  124. interval = setInterval(function() {
  125. let playlist = document.querySelector("#page-manager > ytd-browse > ytd-playlist-header-renderer > div");
  126. if (playlist) {
  127. SetTrigger(playlist).then(() => {clearInterval(interval)});
  128. }
  129. }, Lookup_Delay);
  130. }
  131. }
  132.  
  133. /* 設置判斷 */
  134. async function HideJudgment(element, gm="") {
  135. if (element.style.display === "none" || transform) {
  136. element.style.display = "block";
  137. if (gm !== "") {GM_setValue(gm, false)}
  138. } else {
  139. element.style.display = "none";
  140. if (gm !== "") {GM_setValue(gm, true)}
  141. }
  142. }
  143. /* 設置快捷 */
  144. const interval = setInterval(() => {
  145. const [
  146. end, below, secondary, related, inner,
  147. chat, comments, menu, actions
  148. ] = [
  149. "end", "below", "secondary", "related", "secondary-inner",
  150. "chat-container", "comments", "menu-container", "actions"
  151. ].map(selector => document.getElementById(selector));
  152. if (end && below && secondary && related && inner && chat) {
  153. clearInterval(interval);
  154. document.addEventListener("keydown", function(event) {
  155. if (event.shiftKey) {
  156. event.preventDefault();
  157. let elements = document.querySelectorAll(".ytp-ce-element, .ytp-ce-covering");
  158. elements.forEach(function(element) {
  159. HideJudgment(element);
  160. });
  161. } else if (event.ctrlKey && event.key === "z") {
  162. event.preventDefault();
  163. set = GM_getValue("Minimalist", null);
  164. if (set && set != null) {
  165. end.style.display = "block";
  166. below.style.display = "block";
  167. secondary.style.display = "block";
  168. related.style.display = "block";
  169. GM_setValue("Minimalist", false);
  170. } else {
  171. end.style.display = "none";
  172. below.style.display = "none";
  173. secondary.style.display = "none";
  174. related.style.display = "none";
  175. GM_setValue("Minimalist", true);
  176. }
  177. } else if (event.altKey && event.key === "1") {
  178. event.preventDefault();
  179. let child = inner.childElementCount;
  180. if (child > 1) {// 子元素數量
  181. HideJudgment(chat, "Trigger_1");
  182. HideJudgment(secondary);
  183. HideJudgment(related);
  184. transform = false;
  185. } else {
  186. HideJudgment(chat, "Trigger_1");
  187. HideJudgment(related);
  188. transform = true;
  189. }
  190. } else if (event.altKey && event.key === "2") {
  191. event.preventDefault();
  192. HideJudgment(comments, "Trigger_2");
  193. } else if (event.altKey && event.key === "3") {
  194. event.preventDefault();
  195. HideJudgment(menu, "Trigger_3");
  196. HideJudgment(actions);
  197. } else if (event.altKey && event.key === "4") {
  198. event.preventDefault();
  199. let playlist = document.querySelector("#page-manager > ytd-browse > ytd-playlist-header-renderer > div");
  200. HideJudgment(playlist, "Trigger_4");
  201. }
  202. });
  203. }
  204. }, 500);
  205. }
  206. }
  207. });
  208. observer.observe(document.head, {childList: true, subtree: true});
  209. })();
  210.  
  211. function display_language(language) {
  212. let display = {
  213. "zh-TW": ["📜 設置快捷", `@ 功能失效時 [請重新整理] =>
  214.  
  215. (Shift) : 完全隱藏影片尾部推薦
  216. (Alt + 1) : 隱藏右側影片推薦
  217. (Alt + 2) : 隱藏留言區
  218. (Alt + 3) : 隱藏功能選項
  219. (Alt + 4) : 隱藏播放清單資訊
  220. (Ctrl + Z) : 使用極簡化`],
  221. "zh-CN": ["📜 设置快捷", `@ 功能失效时 [请重新刷新] =>
  222.  
  223. (Shift) : 全部隐藏视频尾部推荐
  224. (Alt + 1) : 隐藏右侧视频推荐
  225. (Alt + 2) : 隐藏评论区
  226. (Alt + 3) : 隐藏功能选项
  227. (Alt + 4) : 隐藏播放列表信息
  228. (Ctrl + Z) : 使用极简化`],
  229. "ja": ["📜 設定ショートカット", `@ 機能が無効になった場合 [再読み込みしてください] =>
  230.  
  231. (Shift) : 動画の最後のおすすめを完全に非表示にする
  232. (Alt + 1) : 右側の動画おすすめを非表示にする
  233. (Alt + 2) : コメント欄を非表示にする
  234. (Alt + 3) : 機能オプションを非表示にする
  235. (Alt + 4) : プレイリスト情報を非表示にする
  236. (Ctrl + Z) : 簡素化を使用する`],
  237. "en": ["📜 Settings Shortcut", `@ When function fails [Please refresh] =>
  238.  
  239. (Shift) : Fully hide video end recommendations
  240. (Alt + 1) : Hide right side video recommendations
  241. (Alt + 2) : Hide comments section
  242. (Alt + 3) : Hide function options
  243. (Alt + 4) : Hide playlist information
  244. (Ctrl + Z) : Use minimalism`],
  245. "ko": ["📜 설정 바로 가기", `@ 기능이 실패하면 [새로 고침하세요] =>
  246.  
  247. (Shift) : 비디오 추천을 완전히 숨기기
  248. (Alt + 1) : 오른쪽 비디오 추천 숨기기
  249. (Alt + 2) : 댓글 섹션 숨기기
  250. (Alt + 3) : 기능 옵션 숨기기
  251. (Alt + 4) : 재생 목록 정보 숨기기
  252. (Ctrl + Z) : 미니멀리즘 사용하기`]};
  253. return display[language] || display["en"];
  254. }