cenko.tech

A Chrome extension that answers YouTube's "Are you still there?"

Keep Playing for YouTube stops YouTube and YouTube Music from pausing with an idle prompt: it keeps the activity timestamp fresh and clicks Yes as a fallback.

Leave YouTube Music playing for an hour without touching the keyboard and it stops with “Are you still there?”. YouTube does the same with “Video paused. Continue watching?”. If the music is on in the background while you work, the first sign is silence, and you have to switch tabs and click Yes.

Keep Playing for YouTube is a small Chrome extension that keeps that from happening. It has no tracking, makes no network requests and loads no remote code. Its only permission is storage, to remember whether it is switched on.

Try it#

It is not on the Chrome Web Store, so you load it from source:

  1. Clone the repository, or download it with Code → Download ZIP and unzip it.
  2. Open chrome://extensions and turn on Developer mode.
  3. Click Load unpacked and select the folder that contains manifest.json.
  4. Reload any YouTube or YouTube Music tabs that were already open.

It also works in Edge, Brave, Opera and other Chromium browsers. There is no build step.

Two layers#

The extension does two things. The first stops the prompt from being triggered. The second dismisses it if it appears anyway.

Prevention: keep the activity timestamp fresh#

YouTube decides you are idle by comparing the current time with window._lact, the timestamp of your last mouse or keyboard input. src/keepalive.js sets that value to the current time once a minute:

const refreshActivity = () => {
  if (!isEnabled()) return;
  window._lact = Date.now();
};

refreshActivity();
setInterval(refreshActivity, INTERVAL_MS);

The interval is 60 seconds because Chrome throttles timers in background tabs to about once a minute, and the music tab is usually a background tab.

Content scripts normally run in an isolated world. They share the page’s DOM but not its JavaScript globals, so a normal content script cannot see window._lact. This script is declared with "world": "MAIN" in the manifest, which runs it in the page’s own JavaScript context:

{
  "matches": ["https://music.youtube.com/*", "https://www.youtube.com/*", "https://m.youtube.com/*"],
  "js": ["src/keepalive.js"],
  "run_at": "document_idle",
  "world": "MAIN"
}

The downside is that chrome.storage is not available in the main world, so this script cannot read the on/off setting itself. The isolated-world script reads the setting and copies it into a data-ytkp-enabled attribute on <html>. Both worlds can see the DOM, so the main-world script reads the attribute.

Fallback: click Yes#

src/content.js checks for a prompt every 1.5 seconds, and again shortly after any pause event, since the prompt pauses the video. Media events don’t bubble, but a listener added in the capture phase on document still receives them.

On YouTube Music the prompt is easy to identify: it is a ytmusic-you-there-renderer element, and its only button is Yes.

YouTube is harder. The idle prompt uses the same yt-confirm-dialog-renderer as other confirmations, such as “Remove from playlist?”. Clicking the confirm button on every dialog would confirm actions you never meant to take. So the extension only clicks when the video is paused and you haven’t clicked, typed, scrolled or touched the page in the last 30 seconds:

const findYouTubePrompt = () => {
  const dialog = firstVisible(document, 'ytd-popup-container yt-confirm-dialog-renderer');
  if (!dialog) return null;
  const video = getVideo();
  if (!video || !video.paused) return null;
  if (Date.now() - lastUserInput < USER_IDLE_MS) return null;
  return firstVisible(dialog, '#confirm-button button, #confirm-button');
};

A dialog you opened yourself appears right after you clicked something, so it never passes the idle check. The idle prompt only appears after a long time without input, so it always does. lastUserInput only counts events with isTrusted set, so synthetic events from scripts don’t reset it.

After a click there is a 3-second cooldown so the same prompt is never clicked twice. If the video is still paused 800 ms after the click, the script calls video.play() itself.

The popup#

The toolbar popup has an on/off switch, saved in chrome.storage.sync so it follows your Chrome profile, and a count of prompts dismissed, saved in chrome.storage.local. Both scripts listen for chrome.storage.onChanged, so turning the switch off takes effect in open tabs straight away, without a reload.

When YouTube changes its markup#

The fallback depends on YouTube’s element names, and those change from time to time. If the prompt starts showing up again, open an issue with the site and, if possible, the prompt’s HTML: right-click the dialog, choose Inspect, then right-click the highlighted element and choose Copy → Copy outerHTML.

cd ~/blog