YouTube: Stop Automatic Video Playback

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

目前為 2021-05-06 提交的版本,檢視 最新版本

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