Greasy Fork is available in English.

Youtube Playback Manuplation

Apr 7, 2024, 4:02:48 PM

// ==UserScript==
// @name        Youtube Playback Manuplation
// @namespace   Violentmonkey Scripts
// @version     2.0
// @grant       none
// @license     GNU GPLv3
// @author      imxitiz
// @match        *://*.youtube.com/*
// @description Apr 7, 2024, 4:02:48 PM
//  - Version 2.0 (Apr 7 2024 4:02:48 PM): Added functionality to remember playback speed across sessions using cookies. Defaults to the saved playback speed if available, otherwise defaults to 2.25x playback speed.
//  - Version 1.0 (Apr 1 2024 7:15:44 PM): Initial release with basic playback manipulation features.
// ==/UserScript==

(function () {
  "use strict";

  let savedPlaybackRate = getCookie("youtubePlaybackRate");

  function waitForKeyElements(
    selectorOrFunction,
    callback,
    waitOnce,
    interval,
    maxIntervals
  ) {
    if (typeof waitOnce === "undefined") {
      waitOnce = true;
    }
    if (typeof interval === "undefined") {
      interval = 300;
    }
    if (typeof maxIntervals === "undefined") {
      maxIntervals = -1;
    }
    var targetNodes =
      typeof selectorOrFunction === "function"
        ? selectorOrFunction()
        : document.querySelectorAll(selectorOrFunction);

    var targetsFound = targetNodes && targetNodes.length > 0;
    if (targetsFound) {
      targetNodes.forEach(function (targetNode) {
        var attrAlreadyFound = "data-userscript-alreadyFound";
        var alreadyFound = targetNode.getAttribute(attrAlreadyFound) || false;
        if (!alreadyFound) {
          var cancelFound = callback(targetNode);
          if (cancelFound) {
            targetsFound = false;
          } else {
            targetNode.setAttribute(attrAlreadyFound, true);
          }
        }
      });
    }

    if (maxIntervals !== 0 && !(targetsFound && waitOnce)) {
      maxIntervals -= 1;
      setTimeout(function () {
        waitForKeyElements(
          selectorOrFunction,
          callback,
          waitOnce,
          interval,
          maxIntervals
        );
      }, interval);
    }
  }

  // Actual code starts here
  let funcDone = false;
  const titleElemSelector = "div#title.style-scope.ytd-watch-metadata";
  const colors = ["#072525", "#287F54", "#C22544"]; // https://www.schemecolor.com/wedding-in-india.php
  if (!funcDone) window.addEventListener("yt-navigate-start", addSpeeds);

  if (document.body && !funcDone) {
    waitForKeyElements(titleElemSelector, addSpeeds);
  }

  function addSpeeds() {
    if (funcDone) return;

    let bgColor = colors[0];
    let moreSpeedsDiv = document.createElement("div");
    moreSpeedsDiv.id = "more-speeds";

    for (let i = 0.25; i < 16; i += 0.25) {
      if (i >= 1) {
        bgColor = colors[1];
      }
      if (i > 4) {
        i += 0.75;
      }
      if (i > 8) {
        i++;
        bgColor = colors[2];
      }

      let btn = document.createElement("button");
      btn.style.backgroundColor = bgColor;
      btn.style.marginRight = "4px";
      btn.style.border = "1px solid #D3D3D3";
      btn.style.borderRadius = "2px";
      btn.style.color = "#ffffff";
      btn.style.cursor = "pointer";
      btn.style.fontFamily = "monospace";
      btn.textContent =
        "×" +
        (i.toString().substr(0, 1) == "0"
          ? i.toString().substr(1)
          : i.toString());
      btn.addEventListener("click", () => {
        document.getElementsByTagName("video")[0].playbackRate = i;
        document.cookie = "youtubePlaybackRate=" + i;
      });
      moreSpeedsDiv.appendChild(btn);
    }

    let titleElem = document.querySelector(titleElemSelector);
    titleElem.after(moreSpeedsDiv);

    if (savedPlaybackRate) {
      changePlayBackRate(parseFloat(savedPlaybackRate));
    } else {
      changePlayBackRate(2.25);
    }

    funcDone = true;
  }

  function changePlayBackRate(speed) {
    try {
      // make default Playback Rate 2.25
      document.getElementsByTagName("video")[0].playbackRate = speed;
    } catch (error) {
      setTimeout(changePlayBackRate, 2000, speed);
    }
  }

  function getCookie(name) {
    const value = `; ${document.cookie}`;
    const parts = value.split(`; ${name}=`);
    if (parts.length === 2) return parts.pop().split(";").shift();
  }
})();