Advertisement
Guest User

userscript YT

a guest
Jun 25th, 2025
39
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.53 KB | Software | 0 0
  1. // ==UserScript==
  2. // @name YT Not Interested Text Option (Homepage + Sidebar)
  3. // @description Adds "Don't Like This" and "Already Watched" buttons to YouTube homepage and sidebar videos.
  4. // @version 1.1
  5. // @match https://www.youtube.com/*
  6. // @noframes
  7. // @grant none
  8. // @author wOxxOm (modified by quetzalcoatl)
  9. // @namespace wOxxOm.scripts
  10. // @license MIT License
  11. // ==/UserScript==
  12. 'use strict';
  13. console.log('[Script: Youtube 1-Click Not-Interested] Started...');
  14. const HOME_CUE = 'ytd-rich-item-renderer'; // Homepage video cue
  15. const SIDEBAR_CUE = 'ytd-compact-video-renderer'; // Sidebar video cue
  16. const CHECK_CUE_FREQUENCY = 2000; // How often to check for the cue
  17. const REPEAT_EVERY = 0; // How often to repeat doStuff; 0 for no repeat
  18. const CONCURRENCY_DELAY = 100;
  19.  
  20. // Add necessary styling
  21. const styleSheet = document.createElement('style');
  22. styleSheet.type = 'text/css';
  23. styleSheet.innerText = `
  24. #metadata-line a {
  25. filter: brightness(85%);
  26. }
  27. `;
  28. document.head.appendChild(styleSheet);
  29.  
  30. // This function sets the visibility attribute of the popups
  31. function setPopupVisibility(visible) {
  32. const popup = document.getElementsByTagName('ytd-popup-container')[0];
  33. popup.style.visibility = visible ? '' : 'hidden';
  34. }
  35.  
  36. /* This function marks videos as i-dont-like or already-watched
  37. *
  38. * if iDontLike is set, the reason will be chosen accordingly. Otherwise,
  39. * reason will be chosen as 'I have already watched this video'.
  40. */
  41. function notInterested(videoBlock, iDontLike = false) {
  42. console.log('[Script: Youtube 1-Click Not-Interested] Processed:', videoBlock);
  43. // Set the popups to be invisible (this is temporary; we'll change it back later)
  44. setPopupVisibility(false);
  45.  
  46. // Click the ellipsis to bring up the menu
  47. const menuEllipsis = videoBlock.getElementsByTagName('yt-icon-button')[0].getElementsByTagName('button')[0];
  48. menuEllipsis.click();
  49.  
  50. // Wait a moment and click on 'Not Interested'
  51. setTimeout(() => {
  52. const menu = document.getElementsByTagName('ytd-menu-popup-renderer')[0];
  53. const notInterested = menu.getElementsByTagName('ytd-menu-service-item-renderer')[4];
  54. notInterested.click();
  55.  
  56. // Wait a moment and select 'Tell Us Why'
  57. setTimeout(() => {
  58. const tellUsWhyButton = videoBlock.getElementsByTagName('ytd-button-renderer')[1]
  59. .getElementsByTagName('button')[0];
  60. tellUsWhyButton.click();
  61.  
  62. // Wait a moment and choose the appropriate reason
  63. setTimeout(() => {
  64. const reasonPopup = document.getElementsByTagName('ytd-dismissal-follow-up-renderer')[0];
  65. const reasonCheckboxes = reasonPopup.getElementsByTagName('tp-yt-paper-checkbox');
  66. const iAlreadyWatchedCheckbox = reasonCheckboxes[0];
  67. const iDontLikeCheckbox = reasonCheckboxes[1];
  68. const submitButton = reasonPopup.getElementsByTagName('ytd-button-renderer')[1];
  69.  
  70. // Click the appropriate checkbox
  71. iDontLike ? iDontLikeCheckbox.click() : iAlreadyWatchedCheckbox.click();
  72.  
  73. // Click Submit
  74. submitButton.click();
  75.  
  76. // Change the popups to visible again
  77. setPopupVisibility(true);
  78. }, CONCURRENCY_DELAY);
  79. }, CONCURRENCY_DELAY);
  80. }, CONCURRENCY_DELAY);
  81. }
  82.  
  83. // The actual stuff; this function adds the new elements to the page
  84. function doStuff() {
  85. console.log('[Script: Youtube 1-Click Not-Interested] invoked main function');
  86.  
  87. // Disable Trusted Types: stackoverflow.com/questions/62810553
  88. if (window.trustedTypes && window.trustedTypes.createPolicy) {
  89. window.trustedTypes.createPolicy('default', {
  90. createHTML: (string, sink) => string,
  91. });
  92. }
  93.  
  94. // Find the blocks/thumbnails of videos on the homepage and sidebar
  95. const homepageVideoBlocks = [...document.getElementsByTagName(HOME_CUE)];
  96. const sidebarVideoBlocks = [...document.getElementsByTagName(SIDEBAR_CUE)];
  97. const videoBlocks = [...homepageVideoBlocks, ...sidebarVideoBlocks];
  98.  
  99. // For every thumbnail...
  100. videoBlocks.forEach((videoBlock) => {
  101. // Find the line that says "X views, Y days ago"
  102. let metadataLine = videoBlock.querySelector('#metadata-line');
  103.  
  104. // Check if the buttons have already been added
  105. const buttonsAlreadyAdded = videoBlock.querySelector('.custom-not-interested-buttons');
  106. if (metadataLine && !buttonsAlreadyAdded) {
  107. // Create the new div
  108. const newDiv = document.createElement('div');
  109. newDiv.className = 'custom-not-interested-buttons'; // Add a unique class
  110. newDiv.innerHTML = `
  111. <div id="metadata-line" class="style-scope ytd-video-meta-block">
  112. <a class="yt-simple-endpoint style-scope yt-formatted-string" href="javascript:void(0)">Don't Like This</a>
  113. <span style="padding: 0px 5px 0px 5px;">•</span>
  114. <a class="yt-simple-endpoint style-scope yt-formatted-string" href="javascript:void(0)">Already Watched</a>
  115. </div>
  116. `;
  117.  
  118. // Insert the new div just below the metadata line we found
  119. metadataLine.parentNode.insertBefore(newDiv, null);
  120.  
  121. // Attach onClick functions to both the links and set iDontLike parameter accordingly
  122. newDiv.getElementsByTagName('a')[0].onclick = () => notInterested(videoBlock, true);
  123. newDiv.getElementsByTagName('a')[1].onclick = () => notInterested(videoBlock);
  124. }
  125. });
  126. }
  127.  
  128. // Function to initialize the MutationObserver
  129. function initMutationObserver() {
  130. const observer = new MutationObserver((mutationsList) => {
  131. for (let mutation of mutationsList) {
  132. if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
  133. // Check if any of the added nodes are video blocks
  134. const addedVideos = [...mutation.addedNodes].filter(
  135. (node) =>
  136. node.tagName === 'YTD-RICH-ITEM-RENDERER' ||
  137. node.tagName === 'YTD-COMPACT-VIDEO-RENDERER'
  138. );
  139. if (addedVideos.length > 0) {
  140. console.log('[Script: Youtube 1-Click Not-Interested] New videos detected, running doStuff...');
  141. doStuff();
  142. }
  143. }
  144. }
  145. });
  146.  
  147. // Start observing the homepage and sidebar containers
  148. const homepageContainer = document.querySelector('ytd-rich-grid-renderer');
  149. const sidebarContainer = document.querySelector('ytd-watch-next-secondary-results-renderer');
  150. if (homepageContainer) {
  151. observer.observe(homepageContainer, { childList: true, subtree: true });
  152. console.log('[Script: Youtube 1-Click Not-Interested] Homepage MutationObserver initialized.');
  153. }
  154. if (sidebarContainer) {
  155. observer.observe(sidebarContainer, { childList: true, subtree: true });
  156. console.log('[Script: Youtube 1-Click Not-Interested] Sidebar MutationObserver initialized.');
  157. }
  158. }
  159.  
  160. // Wait for the initial load of videos
  161. let waitTillLoad = setInterval(function () {
  162. if (document.querySelector(HOME_CUE) || document.querySelector(SIDEBAR_CUE)) {
  163. clearInterval(waitTillLoad);
  164. doStuff();
  165. initMutationObserver();
  166. if (REPEAT_EVERY) setInterval(doStuff, REPEAT_EVERY);
  167. }
  168. }, CHECK_CUE_FREQUENCY);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement