DEV Community

Andrew Elans
Andrew Elans

Posted on

Make F7,F8,F9 to work with Udemy in browser on MacOS

If you follow along with Udemy courses while coding in VS Code (or any other editor), you've probably hit this annoyance: the F7/F8/F9 media keys don't work properly with the Udemy video player. Play/pause fights with itself, and skip forward/back do nothing at all.

Here's why, and two ways to fix it.

The Problem

Udemy's player has a function called syncPlayStateToPlayer that constantly tries to reconcile its internal play/pause state with the actual video element. When you press F8 (play/pause) from another application, the browser's Media Session API toggles the video - but then Udemy's sync function fires and reverts it. The result: nothing happens, or the video flickers.

F7 and F9 (previous/next track) simply aren't wired up at all.

Fix 1: Chrome DevTools Local Override

This is the "proper" local hack. It patches the source file directly so the fix survives page navigation within the course.

  1. Open DevTools (or Safari's Web Inspector)
  2. In the Sources panel, search for a file starting with course-taking-udlite-app...js
  3. Right-click the file and select Override content (Chrome) or add it to Local Overrides
  4. Find the function syncPlayStateToPlayer and add return as the first statement:
this.syncPlayStateToPlayer = () => {
  return // <-- added: short-circuits the sync
  clearTimeout(this.playStateSyncTimeout);
  try {
    if (!this.playingStateNeedsSyncing) {
      return
    }
    if (this.isVideoPlayerInitialized && this.store.isSourceReady) {
      this.isPlaying ? this.play() : this.pause()
    }
    this.playStateSyncTimeout = setTimeout(this.syncPlayStateToPlayer, O.Yj)
  } catch (e) {}
}
Enter fullscreen mode Exit fullscreen mode

The early return kills the sync loop entirely. The rest of the function becomes dead code.

Fix 2: Console Snippet (Recommended)

This is lighter and doesn't require digging through minified source. It does three things:

  1. Walks the React fiber tree from the <video> element to find the component instance
  2. Replaces syncPlayStateToPlayer with a no-op
  3. Registers Media Session handlers for F7 (rewind) and F9 (skip forward)
(() => {
  const v = document.querySelector('video');
  const k = Object.keys(v).find(k => k.startsWith('__reactFiber$'));
  let f = v[k];
  while (f) {
    const i = f.stateNode;
    if (i && i.syncPlayStateToPlayer) {
      i.syncPlayStateToPlayer = () => {};
      console.log('Patched');
      break;
    }
    f = f.return;
  }
  navigator.mediaSession.setActionHandler('previoustrack', () => {
    document.querySelector('[data-purpose="rewind-skip-button"]')?.click();
  });
  navigator.mediaSession.setActionHandler('nexttrack', () => {
    document.querySelector('[data-purpose="forward-skip-button"]')?.click();
  });
})();
Enter fullscreen mode Exit fullscreen mode

How to save it

In Chrome DevTools, go to Sources > Snippets, create a new snippet, paste the code, and save. Run it once after each page load (right-click the snippet > Run, or Ctrl+Enter).

Why This Matters

The real win is workflow. If you're coding along with a Udemy course in VS Code, you can control playback without switching to the browser tab:

  • F8 - play/pause (works after patching the sync)
  • F7 - skip back (mapped to Udemy's rewind button)
  • F9 - skip forward (mapped to Udemy's forward button)

No alt-tabbing. No hunting for the browser. Just press a key and keep coding.

Caveats

  • The snippet needs to be re-run on each full page refresh (navigating between lectures within a course usually doesn't require it)
  • This relies on Udemy's current React internals and data-purpose attributes - it could break if they refactor
  • The local override approach is more persistent but tied to the specific JS bundle filename, which changes on deploys

Top comments (0)