Remove Important YouTube Notifications

Removes the "Important" section in the YouTube notification menu, putting the notifications whithin it back to their appropriate position in the notification list.

目前为 2025-04-22 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Remove Important YouTube Notifications
  3. // @namespace greasyfork.org/en/users/1436613
  4. // @match https://www.youtube.com/*
  5. // @version 1.2.1
  6. // @license MIT
  7. // @author gosha305
  8. // @description Removes the "Important" section in the YouTube notification menu, putting the notifications whithin it back to their appropriate position in the notification list.
  9. // ==/UserScript==
  10.  
  11. let orderOfPrecedence = ["justnow", "secondsago", "minuteago", "minutesago", "hourago", "hoursago", "dayago", "daysago", "weekago", "weeksago", "monthago", "monthsago", "yearago", "yearsago"];
  12. let lookedUpOrderOfPrecedence = false;
  13. let regularNotificationPanel;
  14. let importantNotificationPanel;
  15.  
  16. const hideImportantNotificationsSection = "#sections yt-multi-page-menu-section-renderer:has(ytd-notification-renderer),ytd-popup-container #sections:has(ytd-notification-renderer) #section-title {display:none;} .unimportantNotifications{display:unset !important;}";
  17. (async function(){
  18. //to avoid infinitely waiting for the popup on pages with YouTube embeds
  19. if (!window.location.href.startsWith("https://www.youtube.com/")){
  20. return;
  21. }
  22. let menuRemover = document.createElement("style");
  23. menuRemover.textContent = hideImportantNotificationsSection;
  24. document.head.appendChild(menuRemover);
  25.  
  26. let popupContainer;
  27. function findPopup(resolve){
  28. popupContainer = document.querySelector("ytd-popup-container")
  29. if (!popupContainer){
  30. setTimeout(() => findPopup(resolve), 200);
  31. } else {
  32. resolve();
  33. }
  34. }
  35. await new Promise(findPopup);
  36.  
  37. const popupObserver = new MutationObserver(function(){
  38. const notificationMenu = document.querySelector("ytd-popup-container")?.lastChild
  39. if (notificationMenu?.nodeName == "TP-YT-IRON-DROPDOWN"){
  40. const notificationObserver = new MutationObserver(function(mutationList){replaceNotifications(mutationList);});
  41. notificationObserver.observe(notificationMenu, {attributes: true});
  42. popupObserver.disconnect();
  43. }
  44. });
  45. popupObserver.observe(popupContainer, {childList: true, subtree:true})
  46.  
  47.  
  48. function replaceNotifications(mutationList){
  49. const importantNotifications = document.querySelectorAll("yt-multi-page-menu-section-renderer:nth-of-type(1) #items > ytd-notification-renderer");
  50. if (!importantNotifications || importantNotifications.length == 0) {
  51. return;
  52. }
  53. regularNotificationPanel = document.querySelector("yt-multi-page-menu-section-renderer:nth-of-type(2)");
  54. regularNotificationPanelItems = regularNotificationPanel.querySelector("#items")
  55.  
  56. lookedUpOrderOfPrecedence = false;
  57. importantNotifications.forEach(notification => {regularNotificationPanelItems.insertBefore(notification,findAppropriatePosition(notification))});
  58. regularNotificationPanel.classList.add("unimportantNotifications");
  59. }
  60.  
  61. function findAppropriatePosition(element){
  62. const elementParts = element.querySelector(".metadata.style-scope.ytd-notification-renderer > yt-formatted-string:nth-of-type(2)").textContent.split(" ");
  63. let nonNumberPartsRank = orderOfPrecedence.indexOf(elementParts.filter(e => isNaN(Number(e))).join(''));
  64. if (!lookedUpOrderOfPrecedence && nonNumberPartsRank == -1){
  65. orderOfPrecedence = getOrderOfPrecendence()
  66. lookedUpOrderOfPrecedence = true;
  67. nonNumberPartsRank = orderOfPrecedence.indexOf(elementParts.filter(e => isNaN(Number(e))).join(''));
  68. }
  69. const NumberParts = elementParts.find(e => !isNaN(Number(e)))
  70. const rep = Array.from(regularNotificationPanel.querySelectorAll("ytd-notification-renderer")).find(e => !compareUploadDateText(e.querySelector(".metadata.style-scope.ytd-notification-renderer > yt-formatted-string:nth-of-type(2)").textContent, nonNumberPartsRank, NumberParts));
  71. return rep;
  72. }
  73.  
  74. //look up the order of notifications already in the panel to determine which words represent minutes/hours/days/months/years if the notifications aren't in English
  75.  
  76. function getOrderOfPrecendence(){
  77. const seen = new Set();
  78. const nonNumberDateParts = Array.from(regularNotificationPanel.querySelectorAll("ytd-notification-renderer .metadata.style-scope.ytd-notification-renderer > yt-formatted-string:nth-of-type(2)")).map(e => e.textContent.split(" ").filter(part => isNaN(Number(part))).join(''));
  79. return nonNumberDateParts.filter(e => {if (seen.has(e)) return false; seen.add(e); return true;})
  80. }
  81.  
  82. function compareUploadDateText(date1, nonNumberPartsRank2,date2NumberParts){
  83. const date1Parts = date1.split(" ");
  84. const nonNumberPartsRank1 = orderOfPrecedence.indexOf(date1Parts.filter(e => isNaN(Number(e))).join(''));
  85. if (nonNumberPartsRank1 < nonNumberPartsRank2){
  86. return true;
  87. } else if (nonNumberPartsRank1 > nonNumberPartsRank2){
  88. return false;
  89. } else {
  90. return Number(date1Parts.find(e => !isNaN(Number(e)))) < date2NumberParts
  91. }
  92. }
  93. })();