YouTube URL Tracker Remover

Fixes user-tracking links in the description of YouTube videos

  1. // ==UserScript==
  2. // @name YouTube URL Tracker Remover
  3. // @description Fixes user-tracking links in the description of YouTube videos
  4. // @version 1.2.1
  5. // @author guihkx
  6. // @match https://*.youtube.com/*
  7. // @license MIT; https://opensource.org/licenses/MIT
  8. // @run-at document-start
  9. // @noframes
  10. // @namespace https://github.com/guihkx
  11. // @icon https://s.ytimg.com/yts/img/favicon_48-vflVjB_Qk.png
  12. // @homepageURL https://github.com/guihkx/user-scripts
  13. // @supportURL https://github.com/guihkx/user-scripts/issues
  14. // ==/UserScript==
  15.  
  16. /**
  17. * Changelog:
  18. *
  19. * v1.2.1 (2024-01-18):
  20. * - Detect timestamps more accurately.
  21. *
  22. * v1.2.0 (2022-11-27):
  23. * - Fix compatibility issues with latest YouTube changes
  24. * - Add support for YouTube Shorts
  25. * - Major code refactor
  26. *
  27. * v1.1.2 (2021-10-02):
  28. * - Fix script sometimes not injecting on Firefox with Violentmonkey
  29. *
  30. * v1.1.1 (2021-05-14):
  31. * - Fix wrong selector
  32. *
  33. * v1.1.0 (2021-05-04):
  34. * - Remove support for legacy YouTube (Polymer)
  35. *
  36. * v1.0.0 (2020-04-28):
  37. * - First release
  38. */
  39.  
  40. ;(() => {
  41. 'use strict'
  42.  
  43. const log = console.log.bind(console, '[YouTube URL Tracker Remover]')
  44.  
  45. const ytEvents = [
  46. // Triggered when the page is updated somehow, e.g. by clicking on another video.
  47. 'yt-page-data-updated',
  48. // Triggered when the 'Show less' text under the description is clicked.
  49. 'yt-text-inline-expander-collapse-clicked',
  50. // Triggered when the 'Show more' text under the description is clicked.
  51. 'yt-text-inline-expander-expand-clicked'
  52. ]
  53.  
  54. const ytShortsEvents = [
  55. // Triggered when you open the description box on YouTube Shorts.
  56. 'yt-popup-opened'
  57. ];
  58.  
  59. [...ytEvents, ...ytShortsEvents].forEach(event => {
  60. document.addEventListener(event, async e => {
  61. const isRegularVideo = window.location.pathname === '/watch'
  62. const isShorts = window.location.pathname.startsWith('/shorts/')
  63.  
  64. if ((isRegularVideo && ytShortsEvents.includes(e.type)) || (isShorts && ytEvents.includes(e.type))) {
  65. log(`YouTube event triggered: ${e.type}. Ignoring it because it's not useful on this page.`)
  66. return
  67. }
  68. try {
  69. const selector = isRegularVideo ? '#description-inline-expander a' : '#description a'
  70. removeYoutubeTracking(await querySelectorAllLazy(selector))
  71. } catch (error) {
  72. log('Error: Unable to find links in the video description.\n' +
  73. 'Underlying error: ' + error + '\n' +
  74. 'Possible reasons:\n' +
  75. '- There are currently no links in the video description.\n' +
  76. '- The script is broken (please open a bug report).')
  77. }
  78. })
  79. })
  80.  
  81. function querySelectorAllLazy (selector, intervalMs = 500, maxTries = 6) {
  82. return new Promise((resolve, reject) => {
  83. let tried = 1
  84. const id = setInterval(() => {
  85. if (tried > maxTries) {
  86. clearInterval(id)
  87. reject(new Error(`The maximum amount of tries (${maxTries}) was exceeded.`))
  88. return
  89. }
  90. const elements = document.querySelectorAll(selector)
  91. if (elements.length > 0) {
  92. clearInterval(id)
  93. resolve(elements)
  94. return
  95. }
  96. tried++
  97. }, intervalMs)
  98. })
  99. }
  100.  
  101. function removeYoutubeTracking (links) {
  102. let cleanedUpLinks = 0
  103. for (const link of links) {
  104. // Ignore timestamps
  105. if (Number.isInteger(link.textContent[0])) {
  106. continue
  107. }
  108. // Ignore hashtags
  109. if (link.textContent[0] === '#') {
  110. continue
  111. }
  112. // Ignore mentions
  113. if (link.textContent[0] === '@') {
  114. continue
  115. }
  116. // Ignore URLs within the youtube.com domain
  117. if (link.pathname !== '/redirect') {
  118. continue
  119. }
  120. const actualUrl = new URLSearchParams(link.search).get('q')
  121.  
  122. if (actualUrl === null) {
  123. log('Unable to extract URL from /redirect:', link)
  124. continue
  125. }
  126. link.href = actualUrl
  127. cleanedUpLinks++
  128. }
  129. if (cleanedUpLinks > 0) {
  130. log(`Cleaned up ${cleanedUpLinks} links in the video description.`)
  131. }
  132. }
  133. })()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址