From eb771055d7c5362c6ff8f8ae28369fdfe0e2e706 Mon Sep 17 00:00:00 2001 From: Hermes Admin Date: Mon, 25 May 2026 06:07:38 +0800 Subject: [PATCH] fix: prevent initial playback when autoplay is disabled (#1461) When opening a video in a new tab or via address bar, the video would play for ~1 second before being paused (adding it to watch history). This race condition occurred because YouTube's player started playback before our play() interception could fully take effect on fresh page loads. Fix: Add a 'playing' event listener as a safety net. When autoplay is disabled and no user interaction has occurred, immediately pause the video as soon as the 'playing' event fires. This catches any playback that slips through the HTMLMediaElement.prototype.play() interception gap. Changes: - New ImprovedTube.autoplayPlayingHandler() function that checks autoplay settings and pauses playback if disabled + no user interaction - Safety net 'playing' event listener registered in playerOnPlay() (self-removing after first fire to avoid overhead) - Covers all three autoplay-disable scenarios: player, playlist, channel trailer --- js&css/web-accessible/functions.js | 88 ++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/js&css/web-accessible/functions.js b/js&css/web-accessible/functions.js index d1943de9e..ce1d5406e 100644 --- a/js&css/web-accessible/functions.js +++ b/js&css/web-accessible/functions.js @@ -361,6 +361,30 @@ ImprovedTube.videoPageUpdate = function () { } }; +/*------------------------------------------------------------------------------ +AUTOPLAY SAFETY NET: 'playing' event handler +Catches playback that slips through the play() interception (race condition on +fresh page loads). When autoplay is disabled and user hasn't interacted, pause +immediately before the video renders a visible frame. +------------------------------------------------------------------------------*/ +ImprovedTube.autoplayPlayingHandler = function () { + // Only act when autoplay is disabled for the current context + var shouldBlock = (ImprovedTube.storage.player_autoplay_disable === true && !location.href.includes('list=')) + || (ImprovedTube.storage.playlist_autoplay === false && location.href.includes('list=')) + || (ImprovedTube.storage.channel_trailer_autoplay === false + && ImprovedTube.regex.channel.test(location.href) + && !/(\/(videos|shorts|playlists|community|channels|about|posts|streams|releases))$/.test(location.href)); + + if (shouldBlock && !ImprovedTube.user_interacted) { + var player = ImprovedTube.elements.player || this.closest('.html5-video-player') || this.closest('#movie_player'); + if (player && !player.classList.contains('ad-showing')) { + try { player.pauseVideo(); } catch (e) { this.pause(); } + } + // Remove self after firing — no need to keep listening once we've handled it + this.removeEventListener('playing', ImprovedTube.autoplayPlayingHandler, true); + } +}; + ImprovedTube.playerOnPlay = function () { HTMLMediaElement.prototype.play = (function (original) { return function () { @@ -381,37 +405,41 @@ ImprovedTube.playerOnPlay = function () { this.removeEventListener('ended', ImprovedTube.playerOnEnded, true); this.addEventListener('ended', ImprovedTube.playerOnEnded, true); - /*------------------------------------------------------------------------------ - AUTOPLAY DISABLE player || playlist || channel trailer - ------------------------------------------------------------------------------*/ - if ((((ImprovedTube.storage.player_autoplay_disable === true && !location.href.includes('list=')) - ||(ImprovedTube.storage.playlist_autoplay === false && location.href.includes('list='))) - && location.href.includes('/watch?') // #1703 // (=video page) - )||(ImprovedTube.storage.channel_trailer_autoplay === false && ImprovedTube.regex.channel.test(location.href) - && !/\/(videos|shorts|playlists|community|channels|about|posts|streams|releases)$/.test(location.href)) - ){const player = ImprovedTube.elements.player || this.closest('.html5-video-player') || this.closest('#movie_player'); // #movie_player: outdated since 2024? - if (player && (!ImprovedTube.user_interacted || ImprovedTube.video_url !== location.href) - && !player.classList.contains('ad-showing') // (=no ads playing, needs an update?) - ){ - if (!ImprovedTube.user_interacted) { // (=user didnt click or type) - try { player.pauseVideo(); } catch (error) { this.pause(); } - return Promise.resolve(); - } else { - if (!ImprovedTube._autoplayTimeout) { - ImprovedTube._autoplayTimeout = - setTimeout(() => { - if (!ImprovedTube.user_interacted) { - try { player.pauseVideo(); } catch (error) { this.pause(); } - } ImprovedTube._autoplayTimeout = null; - }, 100); + /*------------------------------------------------------------------------------ + AUTOPLAY DISABLE player || playlist || channel trailer + ------------------------------------------------------------------------------*/ + if ((((ImprovedTube.storage.player_autoplay_disable === true && !location.href.includes('list=')) + ||(ImprovedTube.storage.playlist_autoplay === false && location.href.includes('list='))) + && location.href.includes('/watch?') // #1703 // (=video page) + )||(ImprovedTube.storage.channel_trailer_autoplay === false && ImprovedTube.regex.channel.test(location.href) + && !/(\/(videos|shorts|playlists|community|channels|about|posts|streams|releases))$/.test(location.href)) + ){const player = ImprovedTube.elements.player || this.closest('.html5-video-player') || this.closest('#movie_player'); // #movie_player: outdated since 2024? + if (player && (!ImprovedTube.user_interacted || ImprovedTube.video_url !== location.href) + && !player.classList.contains('ad-showing') // (=no ads playing, needs an update?) + ){ + if (!ImprovedTube.user_interacted) { // (=user didnt click or type) + try { player.pauseVideo(); } catch (error) { this.pause(); } + return Promise.resolve(); + } else { + if (!ImprovedTube._autoplayTimeout) { + ImprovedTube._autoplayTimeout = + setTimeout(() => { + if (!ImprovedTube.user_interacted) { + try { player.pauseVideo(); } catch (error) { this.pause(); } + } ImprovedTube._autoplayTimeout = null; + }, 100); + } + } + // Safety net: listen for 'playing' event to catch playback that slips through + // the play() interception (e.g. on fresh page loads where there's a race condition) + this.removeEventListener('playing', ImprovedTube.autoplayPlayingHandler, true); + this.addEventListener('playing', ImprovedTube.autoplayPlayingHandler, true); + } else { + document.dispatchEvent(new CustomEvent('it-play')); } - } - } else { - document.dispatchEvent(new CustomEvent('it-play')); - } - } else { - document.dispatchEvent(new CustomEvent('it-play')); - } + } else { + document.dispatchEvent(new CustomEvent('it-play')); + } ImprovedTube.playerLoudnessNormalization(); ImprovedTube.playerCinemaModeEnable();