Redgifs media controls

Adds media controls for Redgifs

  1. // ==UserScript==
  2. // @name Redgifs media controls
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.0.0
  5. // @description Adds media controls for Redgifs
  6. // @author You
  7. // @match https://www.redgifs.com/watch/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=redgifs.com
  9. // @license MIT
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. /* jshint esversion: 6 */
  14.  
  15. (function() {
  16. "use strict";
  17. console.info("Redgifs media controls initiated");
  18.  
  19. const SELECTORS = {
  20. MUTE_BUTTON: ".sound",
  21. QUALITY_BUTTON: ".gif-quality",
  22. PLAY_BUTTON: "video",
  23. SEARCH_BOX_ID: "global-search-input"
  24. };
  25.  
  26. var videoElement = getPlayerInSight();
  27.  
  28. function getPlayerInSight() {
  29. const element = document.fullscreenElement || Array.prototype.filter.call(document.querySelectorAll(".player"), isInViewport)[0];
  30. console.log("ELEMENT IN SIGHT", element);
  31. return element || videoElement;
  32. }
  33.  
  34. function videoPlayerRefresher() {
  35. // if video player isn't assigned, look for it again
  36. videoElement = videoElement || getPlayerInSight();
  37. }
  38.  
  39. function isMuted() {
  40. return (videoElement.querySelector(`${SELECTORS.MUTE_BUTTON} > div[class=label]`).innerText === "Off");
  41. }
  42.  
  43. function toggleSound(forceSoundState = false) {
  44. if (forceSoundState) {
  45. if (!isMuted()) {
  46. return;
  47. }
  48. }
  49. videoElement.querySelector(SELECTORS.MUTE_BUTTON)?.click();
  50. }
  51.  
  52. function toggleFullscreen() {
  53. if (document.fullscreen) {
  54. document.exitFullscreen();
  55. } else {
  56. videoElement.requestFullscreen();
  57. }
  58. }
  59.  
  60. function getQuality() {
  61. return videoElement.querySelector(SELECTORS.QUALITY_BUTTON)?.classList[1];
  62. }
  63.  
  64. function toggleQuality(forceHDState = false) {
  65. const qualityButton = videoElement.querySelector(SELECTORS.QUALITY_BUTTON);
  66. if (forceHDState && qualityButton?.classList.contains("hd")) {
  67. return;
  68. }
  69. qualityButton?.click();
  70. }
  71.  
  72. function togglePlayState(forcePlayState) {
  73. const playerElement = videoElement.querySelector(SELECTORS.PLAY_BUTTON);
  74. if (forcePlayState === true) {
  75. playerElement.play();
  76. return;
  77. } else if (forcePlayState === false) {
  78. playerElement.pause();
  79. return;
  80. }
  81. playerElement.click();
  82. }
  83.  
  84. function seekVideo(time, isMarker=false, absolute=false) {
  85. const video = videoElement.querySelector("video");
  86. if (absolute) {
  87. video.currentTime = Number(time);
  88. } else if (!isMarker) {
  89. video.currentTime = Math.max(0, video.currentTime + Number(time));
  90. } else {
  91. const duration = video.duration;
  92. video.currentTime = (time * video.duration) / 10;
  93. }
  94. }
  95.  
  96. function isInViewport(element) {
  97. const rect = element.getBoundingClientRect();
  98. return (rect.y > 0) && (rect.y < (window.innerHeight / 2));
  99. // return ((rect.height + rect.y) > 0);
  100. }
  101.  
  102. function syncPlayerSettings(exportMode = false) {
  103. videoPlayerRefresher();
  104.  
  105. const playerQuality = getQuality();
  106. const playerAudible = !isMuted();
  107.  
  108. if (exportMode) {
  109. localStorage.setItem("quality", playerQuality);
  110. localStorage.setItem("audible", playerAudible);
  111. }
  112.  
  113. const syncedQuality = localStorage.getItem("quality") || playerQuality;
  114. const syncedAudible = (localStorage.getItem("audible") || String(playerAudible)) === "true";
  115. if (syncedQuality !== playerQuality) {
  116. toggleQuality();
  117. }
  118. if (syncedAudible !== playerAudible) {
  119. toggleSound();
  120. }
  121. }
  122.  
  123. document.addEventListener("keydown", e => {
  124. console.log(e.target);
  125. if (e.target.id === SELECTORS.SEARCH_BOX_ID) {
  126. // don't use media controls if user is currently typing in search bar
  127. return;
  128. }
  129. videoPlayerRefresher();
  130. console.log(e.code);
  131. switch(e.code) {
  132. case "KeyF":
  133. toggleFullscreen();
  134. break;
  135. case "KeyM":
  136. toggleSound();
  137. break;
  138. case "KeyQ":
  139. toggleQuality();
  140. break;
  141. case "Space":
  142. e.preventDefault();
  143. e.stopImmediatePropagation();
  144. togglePlayState();
  145. break;
  146. case "KeyA":
  147. toggleFullscreen();
  148. toggleSound(true);
  149. toggleQuality(true);
  150. break;
  151. case "ArrowLeft":
  152. seekVideo(-5);
  153. break;
  154. case "ArrowRight":
  155. seekVideo(5);
  156. break;
  157. case "ArrowUp":
  158. case "ArrowDown":
  159. // this is only to avoid clashes with Redgifs fullscreen behavoiur because their code is poorly written
  160. if (document.fullscreen) {
  161. e.preventDefault();
  162. e.stopImmediatePropagation();
  163. }
  164. break;
  165. case "Digit0":
  166. case "Digit1":
  167. case "Digit2":
  168. case "Digit3":
  169. case "Digit4":
  170. case "Digit5":
  171. case "Digit6":
  172. case "Digit7":
  173. case "Digit8":
  174. case "Digit9":
  175. seekVideo(Number(e.code[5]), true);
  176. break;
  177. default: console.log("Register key press", e.code);
  178. }
  179. });
  180.  
  181. // change player based on scroll position
  182. let lastKnownScrollPosition = 0;
  183. let ticking = false;
  184.  
  185. document.addEventListener('scroll', function(e) {
  186. lastKnownScrollPosition = window.scrollY;
  187.  
  188. if (!ticking) {
  189. window.requestAnimationFrame(function() {
  190. const newVideoElement = getPlayerInSight();
  191. if (videoElement !== newVideoElement) {
  192. console.log("Current video player changed to play", newVideoElement);
  193. }
  194. videoElement = newVideoElement;
  195. ticking = false;
  196. });
  197.  
  198. ticking = true;
  199. }
  200. });
  201.  
  202. window.addEventListener("blur", function(event) {
  203. syncPlayerSettings(true);
  204. }, false);
  205.  
  206. window.addEventListener("focus", function(event) {
  207. window.setTimeout(syncPlayerSettings, 100);
  208. }, false);
  209.  
  210. window.addEventListener("load", (event) => {
  211. syncPlayerSettings();
  212. });
  213. })();