YouTube: Stop Automatic Video Playback

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

当前为 2021-05-01 提交的版本,查看 最新版本

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