Youtube 隐藏工具

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

当前为 2023-09-01 提交的版本,查看 最新版本

  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.20
  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;
  50. RunMaim();
  51.  
  52. /* ======================= 主運行 ========================= */
  53.  
  54. async function RunMaim() {
  55. /* 修改樣式 */
  56. GM_addStyle(`
  57. .ytp-ce-element{opacity: 0.1 !important;}
  58. .ytp-ce-element:hover{opacity: 1 !important;}
  59. `);
  60.  
  61. /* 宣告 */
  62. const VVP_Pattern = /^https:\/\/www\.youtube\.com\/watch\?v=.+$/, // 判斷在播放頁面運行
  63. Playlist_Pattern = /^https:\/\/www\.youtube\.com\/playlist\?list=.+$/, // 判斷在播放清單運行
  64. language = display_language(navigator.language),
  65. ListenerRecord = new Map(),
  66. Lookup_Delay = 300,
  67. Dev = false;
  68.  
  69. /* 註冊菜單 */
  70. GM_registerMenuCommand(language[0], function() {alert(language[1])});
  71.  
  72. /* ======================= 設置 API ========================= */
  73.  
  74. /* 觸發設置 API */
  75. async function SetTrigger(element) {
  76. element.style.display = "none";
  77. return new Promise(resolve => {
  78. if (element.style.display === "none") {resolve(true)}
  79. else {resolve(false)}
  80. });
  81. }
  82.  
  83. /* 設置判斷 API */
  84. async function HideJudgment(element, gm="") {
  85. if (element.style.display === "none" || transform) {
  86. element.style.display = "block";
  87. if (gm !== "") {GM_setValue(gm, false)}
  88. } else {
  89. element.style.display = "none";
  90. if (gm !== "") {GM_setValue(gm, true)}
  91. }
  92. }
  93.  
  94. /* 添加 監聽器 API */
  95. async function addlistener(element, type, listener) {
  96. if (!ListenerRecord.has(element) || !ListenerRecord.get(element).has(type)) {
  97. element.addEventListener(type, listener, true);
  98. if (!ListenerRecord.has(element)) {
  99. ListenerRecord.set(element, new Map());
  100. }
  101. ListenerRecord.get(element).set(type, listener);
  102. }
  103. }
  104.  
  105. /* 等待元素出現 API */
  106. async function WaitElem(selectors, timeout, callback) {
  107. let timer, elements;
  108.  
  109. const observer = new MutationObserver(() => {
  110. elements = selectors.map(selector => document.getElementById(selector));
  111. if (Dev) {console.log(elements)}
  112. if (elements.every(element => element)) {
  113. observer.disconnect();
  114. clearTimeout(timer);
  115. callback(elements);
  116. }
  117. });
  118.  
  119. observer.observe(document.body, { childList: true, subtree: true });
  120. timer = setTimeout(() => {
  121. observer.disconnect();
  122. }, timeout);
  123. }
  124. /* ======================= 讀取設置 ========================= */
  125. const HideElem = ["end", "below", "secondary", "related", "secondary-inner", "chat-container", "comments", "menu-container"];
  126. WaitElem(HideElem, 8000, element => {
  127. const [end, below, secondary, related, inner, chat, comments, menu] = element;
  128.  
  129. /* 獲取設置 */
  130. if (VVP_Pattern.test(currentUrl)) {
  131. // 極簡化
  132. set = GM_getValue("Minimalist", null);
  133. if (set && set !== null) {
  134. Promise.all([SetTrigger(end), SetTrigger(below), SetTrigger(secondary), SetTrigger(related)]).then(results => {
  135. if (results.every(result => result)) {
  136. if (Dev) {console.log("極簡化")}
  137. }
  138. });
  139. } else {
  140. // 推薦播放
  141. set = GM_getValue("Trigger_1", null);
  142. if (set && set !== null){
  143. Promise.all([SetTrigger(chat), SetTrigger(secondary), SetTrigger(related)]).then(results => {
  144. if (results.every(result => result)) {
  145. if (Dev) {console.log("隱藏推薦播放")}
  146. }
  147. });
  148. }
  149. // 留言區
  150. set = GM_getValue("Trigger_2", null);
  151. if (set && set !== null){
  152. SetTrigger(comments).then(() => {
  153. if (Dev) {console.log("隱藏留言區")}
  154. });
  155. }
  156. // 功能選項
  157. set = GM_getValue("Trigger_3", null);
  158. if (set && set !== null){
  159. SetTrigger(menu).then(() => {
  160. if (Dev) {console.log("隱藏功能選項")}
  161. });
  162. }
  163. }
  164. } else if (Playlist_Pattern.test(currentUrl)) {
  165. // 播放清單資訊
  166. set = GM_getValue("Trigger_4", null);
  167. if (set && set !== null){
  168. let interval;
  169. interval = setInterval(function() {
  170. let playlist = document.querySelector("#page-manager > ytd-browse > ytd-playlist-header-renderer > div");
  171. if (playlist) {
  172. SetTrigger(playlist).then(() => {clearInterval(interval)});
  173. }
  174. }, Lookup_Delay);
  175. }
  176. }
  177.  
  178. /* ======================= 快捷設置 ========================= */
  179.  
  180. addlistener(document, "keydown", event => {
  181. if (event.shiftKey) {
  182. event.preventDefault();
  183. let elements = document.querySelectorAll(".ytp-ce-element, .ytp-ce-covering");
  184. elements.forEach(function(element) {
  185. HideJudgment(element);
  186. });
  187. } else if (event.ctrlKey && event.key === "z") {
  188. event.preventDefault();
  189. set = GM_getValue("Minimalist", null);
  190. if (set && set != null) {
  191. end.style.display = "block";
  192. below.style.display = "block";
  193. secondary.style.display = "block";
  194. related.style.display = "block";
  195. GM_setValue("Minimalist", false);
  196. } else {
  197. end.style.display = "none";
  198. below.style.display = "none";
  199. secondary.style.display = "none";
  200. related.style.display = "none";
  201. GM_setValue("Minimalist", true);
  202. }
  203. } else if (event.altKey && event.key === "1") {
  204. event.preventDefault();
  205. let child = inner.childElementCount;
  206. if (child > 1) {// 子元素數量
  207. HideJudgment(chat, "Trigger_1");
  208. HideJudgment(secondary);
  209. HideJudgment(related);
  210. transform = false;
  211. } else {
  212. HideJudgment(chat, "Trigger_1");
  213. HideJudgment(related);
  214. transform = true;
  215. }
  216. } else if (event.altKey && event.key === "2") {
  217. event.preventDefault();
  218. HideJudgment(comments, "Trigger_2");
  219. } else if (event.altKey && event.key === "3") {
  220. event.preventDefault();
  221. HideJudgment(menu, "Trigger_3");
  222. } else if (event.altKey && event.key === "4") {
  223. event.preventDefault();
  224. let playlist = document.querySelector("#page-manager > ytd-browse > ytd-playlist-header-renderer > div");
  225. HideJudgment(playlist, "Trigger_4");
  226. }
  227. })
  228. });
  229. }
  230. }
  231. });
  232. /* 啟用觀察 */
  233. observer.observe(document.head, {childList: true, subtree: true});
  234.  
  235. /* ======================= 語言設置 ========================= */
  236.  
  237. function display_language(language) {
  238. let display = {
  239. "zh-TW": ["📜 設置快捷", `@ 功能失效時 [請重新整理] =>
  240.  
  241. (Shift) : 完全隱藏影片尾部推薦
  242. (Alt + 1) : 隱藏右側影片推薦
  243. (Alt + 2) : 隱藏留言區
  244. (Alt + 3) : 隱藏功能選項
  245. (Alt + 4) : 隱藏播放清單資訊
  246. (Ctrl + Z) : 使用極簡化`],
  247.  
  248. "zh-CN": ["📜 设置快捷", `@ 功能失效时 [请重新刷新] =>
  249. (Shift) : 全部隐藏视频尾部推荐
  250. (Alt + 1) : 隐藏右侧视频推荐
  251. (Alt + 2) : 隐藏评论区
  252. (Alt + 3) : 隐藏功能选项
  253. (Alt + 4) : 隐藏播放列表信息
  254. (Ctrl + Z) : 使用极简化`],
  255.  
  256. "ja": ["📜 設定ショートカット", `@ 機能が無効になった場合 [再読み込みしてください] =>
  257. (Shift) : 動画の最後のおすすめを完全に非表示にする
  258. (Alt + 1) : 右側の動画おすすめを非表示にする
  259. (Alt + 2) : コメント欄を非表示にする
  260. (Alt + 3) : 機能オプションを非表示にする
  261. (Alt + 4) : プレイリスト情報を非表示にする
  262. (Ctrl + Z) : 簡素化を使用する`],
  263.  
  264. "en-US": ["📜 Settings Shortcut", `@ When function fails [Please refresh] =>
  265. (Shift) : Fully hide video end recommendations
  266. (Alt + 1) : Hide right side video recommendations
  267. (Alt + 2) : Hide comments section
  268. (Alt + 3) : Hide function options
  269. (Alt + 4) : Hide playlist information
  270. (Ctrl + Z) : Use minimalism`],
  271.  
  272. "ko": ["📜 설정 바로 가기", `@ 기능이 실패하면 [새로 고침하세요] =>
  273. (Shift) : 비디오 추천을 완전히 숨기기
  274. (Alt + 1) : 오른쪽 비디오 추천 숨기기
  275. (Alt + 2) : 댓글 섹션 숨기기
  276. (Alt + 3) : 기능 옵션 숨기기
  277. (Alt + 4) : 재생 목록 정보 숨기기
  278. (Ctrl + Z) : 미니멀리즘 사용하기`]};
  279.  
  280. return display[language] || display["en-US"];
  281. }
  282. })();