YouTube: Stop Automatic Video Playback

Stop automatic video playback everywhere. Works on first page load & after navigating.

当前为 2022-06-14 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube: Stop Automatic Video Playback
  3. // @namespace org.sidneys.userscripts
  4. // @homepage https://gist.githubusercontent.com/sidneys/02a9025ae1f23aefe1f4ea02e78b0ac8/raw/
  5. // @version 4.7.3
  6. // @description Stop automatic video playback everywhere. Works on first page load & after navigating.
  7. // @author sidneys
  8. // @icon https://www.youtube.com/favicon.ico
  9. // @noframes
  10. // @match http*://www.youtube.com/*
  11. // @require https://greasyfork.org/scripts/38888-greasemonkey-color-log/code/Greasemonkey%20%7C%20Color%20Log.js
  12. // @run-at document-start
  13. // ==/UserScript==
  14.  
  15.  
  16. /**
  17. * ESLint
  18. * @global
  19. */
  20. /* global Debug */
  21. Debug = false
  22.  
  23.  
  24. /**
  25. * Applicable URL paths
  26. * @default
  27. * @constant
  28. */
  29. const urlPathList = [
  30. '/channel',
  31. '/watch'
  32. ]
  33.  
  34.  
  35. /**
  36. * YouTube API Player States
  37. * @constant
  38. * @enum
  39. */
  40. const PLAYERSTATE = {
  41. '-1': 'UNSTARTED',
  42. 0: 'ENDED',
  43. 1: 'PLAYING',
  44. 2: 'PAUSED',
  45. 3: 'BUFFERING',
  46. 5: 'CUED'
  47. }
  48.  
  49.  
  50. /**
  51. * Generate a method name for an event name using the DOM convention ("on" + Event Name)
  52. * @param {String} eventName - Event name (e.g. 'Click')
  53. * @returns {String} - Method name (e.g. 'onclick')
  54. */
  55. let getHandlerMethodNameForEventName = eventName => `on${eventName.toLowerCase()}`
  56.  
  57. /**
  58. * Lookup the first <video> Element
  59. * @returns {Element} - <video> Element
  60. */
  61. let getVideoElement = () => document.querySelector('video')
  62.  
  63. /**
  64. * Lookup YouTube Video Player through the DOM
  65. * @returns {Object} - YouTube Video Player
  66. */
  67. let getYoutubePlayer = () => {
  68. console.debug('getYoutubePlayer')
  69.  
  70. // Lookup Player element
  71. const playerElement = document.querySelector('ytd-player')
  72.  
  73. // Return the property containing the Player API
  74. return playerElement && playerElement.player_
  75. }
  76.  
  77. /**
  78. * Stop playback on YouTube via the Player API
  79. * @param {Object} youtubePlayer - YouTube Video Player API
  80. */
  81. let stopYoutubePlayerPlayback = (youtubePlayer) => {
  82. console.debug('stopYoutubePlayerPlayback')
  83.  
  84. // Get YouTube Video element
  85. const videoElement = getVideoElement()
  86.  
  87. // Playback event types to watch
  88. const eventTypeList = [ 'play', 'playing', 'timeupdate' ]
  89.  
  90. // Iterate playback event types
  91. eventTypeList.forEach((eventType, eventTypeIndex) => {
  92.  
  93. // Playback "Stopper" method, each playback event
  94. let eventHandler = () => {
  95. console.debug(`videoElement#${eventType}`)
  96.  
  97. // Remove all "Stopper" event handlers by deleting <video>#onplay, <video>#onplaying, <video>#ontimeupdate
  98. eventTypeList.forEach((eventType) => {
  99. const handlerMethodName = getHandlerMethodNameForEventName(eventType)
  100.  
  101. delete videoElement[handlerMethodName]
  102. videoElement[handlerMethodName] = null
  103.  
  104. // DEBUG
  105. console.debug('videoElement', 'removing event handler method:', handlerMethodName)
  106. })
  107.  
  108. // Lookup YouTube Player state
  109. const playerState = youtubePlayer.getPlayerState()
  110.  
  111. // Stop video (if it is not already paused)
  112. if (youtubePlayer.getPlayerState() !== 2) {
  113. youtubePlayer.pauseVideo()
  114.  
  115. // Status
  116. console.info('Stopped automatic video playback', 'during the', PLAYERSTATE[playerState], 'phase')
  117.  
  118. // DEBUG
  119. console.debug('stopYoutubePlayerPlayback', 'eventType:', eventType, 'playerState:', `${playerState} (${PLAYERSTATE[playerState]})`)
  120. }
  121. }
  122.  
  123. // Add event handler to video element
  124. const handlerMethodName = getHandlerMethodNameForEventName(eventType)
  125. videoElement[handlerMethodName] = eventHandler
  126. })
  127. }
  128.  
  129.  
  130. /**
  131. * Init
  132. */
  133. let init = () => {
  134. console.info('init')
  135.  
  136. // Verify URL path
  137. if (!urlPathList.some(urlPath => window.location.pathname.startsWith(urlPath))) { return }
  138.  
  139. // Initiate lookup loop
  140. let requestId
  141. let lookup = () => {
  142. // Lookup YouTube Player
  143. const youtubePlayer = getYoutubePlayer()
  144.  
  145. // Is 1. the Player API available, 2. the Player ready?
  146. if (!youtubePlayer || (youtubePlayer && !youtubePlayer.isReady())) {
  147. // DEBUG
  148. console.debug('❌ YouTube Player API unavailable or Player not ready yet.')
  149.  
  150. // Skip loop
  151. requestId = window.requestAnimationFrame(lookup)
  152.  
  153. return
  154. }
  155.  
  156. // Stop Playback
  157. stopYoutubePlayerPlayback(youtubePlayer)
  158.  
  159. // DEBUG
  160. console.debug('✅ YouTube Player API available and Player ready.')
  161.  
  162. // End loop
  163. window.cancelAnimationFrame(requestId)
  164. }
  165.  
  166. // Initiate loop
  167. requestId = window.requestAnimationFrame(lookup)
  168. }
  169.  
  170.  
  171. /**
  172. * Handle in-page navigation (modern YouTube)
  173. * @listens window:Event#yt-navigate-finish
  174. */
  175. window.addEventListener('yt-navigate-finish', () => {
  176. console.debug('window#yt-navigate-finish')
  177.  
  178. init()
  179. })