YouTube: Stop Automatic Video Playback

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

// ==UserScript==
// @name            YouTube: Stop Automatic Video Playback
// @namespace       org.sidneys.userscripts
// @homepage        https://gist.githubusercontent.com/sidneys/02a9025ae1f23aefe1f4ea02e78b0ac8/raw/
// @version         4.7.3
// @description     Stop automatic video playback everywhere. Works on first page load & after navigating.
// @author          sidneys
// @icon            https://www.youtube.com/favicon.ico
// @noframes
// @match           http*://www.youtube.com/*
// @require         https://greasyfork.org/scripts/38888-greasemonkey-color-log/code/Greasemonkey%20%7C%20Color%20Log.js
// @run-at          document-start
// ==/UserScript==


/**
 * ESLint
 * @global
 */
/* global Debug */
Debug = false


/**
 * Applicable URL paths
 * @default
 * @constant
 */
const urlPathList = [
    '/channel',
    '/watch'
]


/**
 * YouTube API Player States
 * @constant
 * @enum
 */
const PLAYERSTATE = {
    '-1': 'UNSTARTED',
    0: 'ENDED',
    1: 'PLAYING',
    2: 'PAUSED',
    3: 'BUFFERING',
    5: 'CUED'
}


/**
 * Generate a method name for an event name using the DOM convention ("on" + Event Name)
 * @param {String} eventName - Event name (e.g. 'Click')
 * @returns {String} - Method name (e.g. 'onclick')
 */
let getHandlerMethodNameForEventName = eventName => `on${eventName.toLowerCase()}`

/**
 * Lookup the first <video> Element
 * @returns {Element} - <video> Element
 */
let getVideoElement = () => document.querySelector('video')

/**
 * Lookup YouTube Video Player through the DOM
 * @returns {Object} - YouTube Video Player
 */
let getYoutubePlayer = () => {
    console.debug('getYoutubePlayer')

    // Lookup Player element
    const playerElement = document.querySelector('ytd-player')

    // Return the property containing the Player API
    return playerElement && playerElement.player_
}

/**
 * Stop playback on YouTube via the Player API
 * @param {Object} youtubePlayer - YouTube Video Player API
 */
let stopYoutubePlayerPlayback = (youtubePlayer) => {
    console.debug('stopYoutubePlayerPlayback')

    // Get YouTube Video element
    const videoElement = getVideoElement()

    // Playback event types to watch
    const eventTypeList = [ 'play', 'playing', 'timeupdate' ]

    // Iterate playback event types
    eventTypeList.forEach((eventType, eventTypeIndex) => {

        // Playback "Stopper" method, each playback event
        let eventHandler = () => {
            console.debug(`videoElement#${eventType}`)

            // Remove all "Stopper" event handlers by deleting <video>#onplay, <video>#onplaying, <video>#ontimeupdate
            eventTypeList.forEach((eventType) => {
                const handlerMethodName = getHandlerMethodNameForEventName(eventType)

                delete videoElement[handlerMethodName]
                videoElement[handlerMethodName] = null

                // DEBUG
                console.debug('videoElement', 'removing event handler method:', handlerMethodName)
            })

            // Lookup YouTube Player state
            const playerState = youtubePlayer.getPlayerState()

            // Stop video (if it is not already paused)
            if (youtubePlayer.getPlayerState() !== 2) {
                youtubePlayer.pauseVideo()

                // Status
                console.info('Stopped automatic video playback', 'during the', PLAYERSTATE[playerState], 'phase')

                // DEBUG
                console.debug('stopYoutubePlayerPlayback', 'eventType:', eventType, 'playerState:', `${playerState} (${PLAYERSTATE[playerState]})`)
            }
        }

        // Add event handler to video element
        const handlerMethodName = getHandlerMethodNameForEventName(eventType)
        videoElement[handlerMethodName] = eventHandler
    })
}


/**
 * Init
 */
let init = () => {
    console.info('init')

    // Verify URL path
    if (!urlPathList.some(urlPath => window.location.pathname.startsWith(urlPath))) { return }

    // Initiate lookup loop
    let requestId
    let lookup = () => {
        // Lookup YouTube Player
        const youtubePlayer = getYoutubePlayer()

        // Is 1. the Player API available,  2. the Player ready?
        if (!youtubePlayer || (youtubePlayer && !youtubePlayer.isReady())) {
            // DEBUG
            console.debug('❌ YouTube Player API unavailable or Player not ready yet.')

            // Skip loop
            requestId = window.requestAnimationFrame(lookup)

            return
        }

        // Stop Playback
        stopYoutubePlayerPlayback(youtubePlayer)

        // DEBUG
        console.debug('✅ YouTube Player API available and Player ready.')

        // End loop
        window.cancelAnimationFrame(requestId)
    }

    // Initiate loop
    requestId = window.requestAnimationFrame(lookup)
}


/**
 * Handle in-page navigation (modern YouTube)
 * @listens window:Event#yt-navigate-finish
 */
window.addEventListener('yt-navigate-finish', () => {
    console.debug('window#yt-navigate-finish')

    init()
})