Prevents Youtube from auto-pausing playlist videos
// ==UserScript==
// @name Youtube Auto-Pause Bypass
// @namespace http://tampermonkey.net/
// @version 3.2
// @description Prevents Youtube from auto-pausing playlist videos
// @author Nestradama
// @match *://*.youtube.com/*
// @match *://youtube.com/*
// @run-at document-end
// @grant GM_setValue
// @grant GM_getValue
// @license GNU GPLv3
// ==/UserScript==
(function () {
console.log("UserScript Auto Pause Bypass - LOADED")
console.log("Thank you for using my script! Do not hesitate to reach me if you have any issues with it (nestradama on Discord)")
'use strict';
let isEnabled = GM_getValue('isEnabled', true);
let lastManualIntent = 0;
//Toggle button to disable the plugin temporarily to delete a comment or anything else that would prompt a modal dialog that you wouldnt want to close
function injectToggleButton() {
const btn = document.createElement('button');
btn.id = 'afk-bypass-toggle';
btn.textContent = "Auto-Pause Bypass: ON";
Object.assign(btn.style, {
margin: '0 8px',
padding: '6px 12px',
background: '#212121',
color: '#fff',
border: '#ba0000 2px solid',
borderRadius: '6px',
cursor: 'pointer',
fontFamily: 'sans-serif',
fontSize: '1.2rem',
alignSelf: 'center',
});
btn.addEventListener('click', () => {
isEnabled = !isEnabled;
GM_setValue('isEnabled', isEnabled);
btn.textContent = isEnabled ? "Auto-Pause Bypass: ON" : "Auto-Pause Bypass: OFF";
btn.style.background = isEnabled ? '#212121' : '#d50000';
console.log(`AFK Bypass ${isEnabled ? 'enabled' : 'disabled'}`);
});
const isMusic = location.hostname === 'music.youtube.com';
const tryInject = setInterval(() => {
const target = isMusic
? document.querySelector('[slot="nav-bar"] #right-content')
: document.querySelector('#start');
if (target) {
if (isMusic) {
// Insert as 2nd child (before index 1); fall back to append if fewer than 2 children exist
target.insertBefore(btn, target.children[1] ?? null);
} else {
target.appendChild(btn);
}
clearInterval(tryInject);
btn.textContent = isEnabled ? "Auto-Pause Bypass: ON" : "Auto-Pause Bypass: OFF";
btn.style.background = isEnabled ? '#212121' : '#d50000';
// Just to make sure the button doesnt resize when changing state, it bothered me, also why are you still reading this
btn.textContent = "Auto-Pause Bypass: OFF";
const offWidth = btn.offsetWidth;
btn.textContent = "Auto-Pause Bypass: ON";
const onWidth = btn.offsetWidth;
btn.style.minWidth = Math.max(offWidth, onWidth) + 'px';
btn.textContent = isEnabled ? "Auto-Pause Bypass: ON" : "Auto-Pause Bypass: OFF";
}
}, 300);
}
if (document.body) {
injectToggleButton();
} else {
document.addEventListener('DOMContentLoaded', injectToggleButton);
}
// Logic
document.addEventListener('keydown', e => {
if (e.key === ' ' || e.key === 'k' || e.key === 'K')
lastManualIntent = Date.now();
}, true);
document.addEventListener('click', e => {
if (e.target?.closest?.('#movie_player, ytd-player'))
lastManualIntent = Date.now();
}, true);
const isManual = () => Date.now() - lastManualIntent < 1500;
function resume() {
if (!isEnabled) return;
document.querySelectorAll('video').forEach(v => {
if (v.paused && !v.ended) v.play().catch(() => {});
});
console.log("Resumed");
}
function closeAfkDialog() {
if (!isEnabled) return;
document.querySelectorAll('tp-yt-paper-dialog').forEach(d => {
if (d.hasAttribute('opened') || d.opened) {
const hasConfirm =
d.querySelector('yt-confirm-dialog-renderer') ||
d.shadowRoot?.querySelector('yt-confirm-dialog-renderer');
if (hasConfirm && d.close) d.close();
}
});
document.querySelectorAll('ytd-popup-container').forEach(el => {
const inst = el.polymerController || el.inst || el;
if (inst?.handleClosePopupAction_) {
try { inst.handleClosePopupAction_('yt-confirm-dialog-renderer'); } catch (_) {}
}
});
}
// First Layer
document.addEventListener('yt-popup-opened', function (e) {
try {
const target = e.composedPath?.()[0] ?? e.target;
if (target?.tagName?.toLowerCase() === 'yt-confirm-dialog-renderer') {
closeAfkDialog();
resume();
}
} catch (_) {}
}, true);
// Second Layer
document.addEventListener('iron-overlay-opened', function (e) {
try {
const dialog = e.target;
if (!dialog) return;
const hasConfirm =
dialog.querySelector?.('yt-confirm-dialog-renderer') ||
dialog.shadowRoot?.querySelector('yt-confirm-dialog-renderer');
if (hasConfirm) {
closeAfkDialog();
resume();
}
} catch (_) {}
}, true);
// Third layer
function attachPlayerObserver(player) {
if (player.__afkWatching) return;
player.__afkWatching = true;
new MutationObserver(mutations => {
for (const m of mutations) {
if (m.attributeName !== 'class') continue;
const justPaused =
!m.oldValue?.includes('paused-mode') &&
player.classList.contains('paused-mode');
if (justPaused && !isManual()) {
resume();
}
}
}).observe(player, {
attributes: true,
attributeOldValue: true,
attributeFilter: ['class'],
});
}
function tryAttach() {
const player = document.querySelector('#movie_player');
if (player) attachPlayerObserver(player);
}
tryAttach();
document.addEventListener('yt-navigate-finish', () => {
setTimeout(tryAttach, 800);
});
new MutationObserver(tryAttach)
.observe(document.documentElement, { childList: true, subtree: true });
})();