Video Background Play Fix

Prevents YouTube and Vimeo from pausing videos when minimizing or switching tabs. Cross-browser port of https://addons.mozilla.org/en-US/firefox/addon/video-background-play-fix/

  1. // ==UserScript==
  2. // @name Video Background Play Fix
  3. // @namespace https://greasyfork.org/en/users/50-couchy
  4. // @version 20220119
  5. // @description Prevents YouTube and Vimeo from pausing videos when minimizing or switching tabs. Cross-browser port of https://addons.mozilla.org/en-US/firefox/addon/video-background-play-fix/
  6. // @author Couchy
  7. // @match *://*.youtube.com/*
  8. // @match *://*.vimeo.com/*
  9. // @grant none
  10. // @run-at document-start
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. const IS_YOUTUBE = window.location.hostname.search(/(?:^|.+\.)youtube\.com/) > -1 ||
  17. window.location.hostname.search(/(?:^|.+\.)youtube-nocookie\.com/) > -1;
  18. const IS_MOBILE_YOUTUBE = window.location.hostname == 'm.youtube.com';
  19. const IS_DESKTOP_YOUTUBE = IS_YOUTUBE && !IS_MOBILE_YOUTUBE;
  20. const IS_VIMEO = window.location.hostname.search(/(?:^|.+\.)vimeo\.com/) > -1;
  21.  
  22. const IS_ANDROID = window.navigator.userAgent.indexOf('Android') > -1;
  23.  
  24. // Page Visibility API
  25. if (IS_ANDROID || !IS_DESKTOP_YOUTUBE) {
  26. Object.defineProperties(document,
  27. { 'hidden': {value: false}, 'visibilityState': {value: 'visible'} });
  28. }
  29.  
  30. window.addEventListener(
  31. 'visibilitychange', evt => evt.stopImmediatePropagation(), true);
  32.  
  33. // Fullscreen API
  34. if (IS_VIMEO) {
  35. window.addEventListener(
  36. 'fullscreenchange', evt => evt.stopImmediatePropagation(), true);
  37. }
  38.  
  39. // User activity tracking
  40. if (IS_YOUTUBE) {
  41. loop(pressKey, 60 * 1000, 10 * 1000); // every minute +/- 10 seconds
  42. }
  43.  
  44. function pressKey() {
  45. const key = 18;
  46. sendKeyEvent("keydown", key);
  47. sendKeyEvent("keyup", key);
  48. }
  49.  
  50. function sendKeyEvent (aEvent, aKey) {
  51. document.dispatchEvent(new KeyboardEvent(aEvent, {
  52. bubbles: true,
  53. cancelable: true,
  54. keyCode: aKey,
  55. which: aKey,
  56. }));
  57. }
  58.  
  59. function loop(aCallback, aDelay, aJitter) {
  60. let jitter = getRandomInt(-aJitter/2, aJitter/2);
  61. let delay = Math.max(aDelay + jitter, 0);
  62.  
  63. window.setTimeout(() => {
  64. aCallback();
  65. loop(aCallback, aDelay, aJitter);
  66. }, delay);
  67. }
  68.  
  69. function getRandomInt(aMin, aMax) {
  70. let min = Math.ceil(aMin);
  71. let max = Math.floor(aMax);
  72. return Math.floor(Math.random() * (max - min)) + min;
  73. }
  74.  
  75. })();