Enhanced player controls for piped.video

Enhances the piped.video player controls with tooltips on hover, looping, fast forward, rewind, seek forwards 10s, seek backwards 10s, etc.

// ==UserScript==
// @name        Enhanced player controls for piped.video
// @namespace   Violentmonkey Scripts
// @match       https://piped.video/watch
// @grant       none
// @version     0.0.1
// @author      William Grant
// @license MIT
// @description Enhances the piped.video player controls with tooltips on hover, looping, fast forward, rewind, seek forwards 10s, seek backwards 10s, etc.
// ==/UserScript==

// Acknowledgements:
//   Thanks to SchubmannM for giving me a starting point for this script.

// References:
//   https://greasyfork.org/en/scripts/471587-oqee-tv-enable-video-seek-bar/code
//   https://shaka-player-demo.appspot.com/docs/api/tutorial-ui-customization.html

(function () {
  "use strict";

  const INTERVAL_TIME = 500; // milliseconds

  // This function configures the Shaka Player UI
  function configureShakaPlayerUI(videoElement) {
    const videoUI = videoElement["ui"];

    // Grab the current config and update it
    const currentConfig = videoUI.getConfiguration();
    const controlPanelElements = currentConfig.controlPanelElements;
    controlPanelElements.splice(0, 0, "rewind");
    controlPanelElements.splice(2, 0, "fast_forward");
    controlPanelElements.splice(-2, 0, "loop");

    const config = {
      ...currentConfig,
      controlPanelElements,
      enableTooltips: true,
    };

    // Configure the Shaka Player UI with the config object
    videoUI.configure(config);
  }

  function addSeekButtons() {
    const seekBackwardButton = document.createElement("button");
    seekBackwardButton.classList.add(
      "material-icons-round",
      "shaka-seek-backward-button",
      "shaka-tooltip-status"
    );
    seekBackwardButton.setAttribute("aria-label", "Seek Backwards");
    seekBackwardButton.setAttribute("shaka-status", "10s");
    seekBackwardButton.textContent = "↩";
    seekBackwardButton.addEventListener("click", function (event) {
      const videoElement = document.getElementsByTagName("video").item(0);
      videoElement.currentTime = videoElement.currentTime - 10;
      event.preventDefault();
      event.stopPropagation();
    });

    const seekForwardButton = document.createElement("button");
    seekForwardButton.classList.add(
      "material-icons-round",
      "shaka-seek-forward-button",
      "shaka-tooltip-status"
    );
    seekForwardButton.setAttribute("aria-label", "Seek Forwards");
    seekForwardButton.setAttribute("shaka-status", "10s");
    seekForwardButton.textContent = "↪";
    seekForwardButton.addEventListener("click", function (event) {
      const videoElement = document.getElementsByTagName("video").item(0);
      videoElement.currentTime = videoElement.currentTime + 10;
      event.preventDefault();
      event.stopPropagation();
    });

    // Add Seek buttons into controls at the correct positions
    const controls = document.querySelector(".shaka-controls-button-panel");
    const fastForwardButton = document.querySelector(
      "button.material-icons-round.shaka-fast-forward-button"
    );
    const playButton = document.querySelector(
      "button.material-icons-round.shaka-small-play-button"
    );
    controls.insertBefore(seekForwardButton, fastForwardButton);
    controls.insertBefore(seekBackwardButton, playButton);
  }

  // This function will be executed every INTERVAL_TIME milliseconds until the video player and its UI are fully loaded
  function checkVideoAndUI() {
    try {
      // Try to find the video player element in the DOM
      const videoElement = document.getElementsByTagName("video").item(0);

      // Check if the video player element and its 'ui' property both exist
      if (videoElement && videoElement.ui) {
        // If they do, clear the interval to stop executing this function
        clearInterval(intervalId);

        // Then, configure the Shaka Player UI using the video element
        configureShakaPlayerUI(videoElement);

        // Then, add seek buttons to the Shaka Player UI
        addSeekButtons();
      }
    } catch (err) {
      console.error("Piped enhanced player controls error", err);
    }
  }

  // Start the interval, storing its ID in 'intervalId'
  const intervalId = setInterval(checkVideoAndUI, INTERVAL_TIME);
})();