视频倍速工具

除youtube以外按c加速,按x减速,按z复位,youtube按v加速。点击右上角油猴按钮可开关视频速度记忆功能。

当前为 2023-11-19 提交的版本,查看 最新版本

// ==UserScript==
// @name         视频倍速工具
// @namespace    http://tampermonkey.net/
// @version      0.2.8
// @description  除youtube以外按c加速,按x减速,按z复位,youtube按v加速。点击右上角油猴按钮可开关视频速度记忆功能。
// @author       call duck
// @match        *://*/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=bilibili.com
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addValueChangeListener
// @run-at document-end
// @license MIT
// ==/UserScript==

// 使用方法:除youtube以外按c加速,按x减速,按z复位,youtube按v加速。

(function () {
  "use strict";

  if (window.innerWidth < 100) {
    return;
  }

  console.log(document.getElementsByTagName('video'))

  const REMEMBER_KEY = `${location.hostname}_da4e1a5f0c5b46cdadadf70af3c19f37`;
  let isRememberSpeed = GM_getValue(REMEMBER_KEY, false);

  const SPEED_KEY = "2779054e81654c2990dd9d2bfe9202aa";
  const originSpeed = localStorage.getItem(SPEED_KEY);
  let speed;
  if (isNaN(originSpeed) || !originSpeed) {
    speed = 1;
    localStorage.setItem(SPEED_KEY, 1);
  } else if (isRememberSpeed) {
    speed = originSpeed;
  } else {
    speed = 1;
  }
  speed = parseFloat(parseFloat(speed).toFixed(2));
  let videos;
  const step = 0.05;

  let menuId;

  function addRememberMenu() {
    if (!isRememberSpeed) {
      addOpenRememberMenu();
    } else {
      addCloseRememberMenu();
    }

    GM_addValueChangeListener(
      REMEMBER_KEY,
      function name(key, oldValue, newValue) {
        GM_unregisterMenuCommand(menuId);
        if (newValue === true) {
          addCloseRememberMenu();
        } else {
          addOpenRememberMenu();
        }
      }
    );
  }
  addRememberMenu();

  async function addOpenRememberMenu() {
    if (menuId) GM_unregisterMenuCommand(menuId);
    menuId = await GM.registerMenuCommand("打开视频速度记忆功能", async () => {
      GM_setValue(REMEMBER_KEY, true);
      videos.forEach((v)=>{
        notify(v,"视频播放速度记忆功能已打开");
      })
    });
  }

  async function addCloseRememberMenu() {
    if (menuId) GM_unregisterMenuCommand(menuId);
    menuId = await GM.registerMenuCommand("关闭视频速度记忆功能", async () => {
      GM_setValue(REMEMBER_KEY, false);
      videos.forEach((v)=>{
        notify(v,"视频播放速度记忆功能已关闭");
      })
    });
  }

  videos = Array.prototype.slice.call(document.getElementsByTagName("video"));

  new Promise((res) => {
    if (!videos) {
      let count = 0;
      const maxCount = 3;
      function _getVideo() {
        setTimeout(() => {
          videos = getVideo();
          count++;
          if (!videos && count < maxCount) {
            _getVideo();
          } else {
            res(videos);
          }
        }, 1000);
      }
      _getVideo();
    } else {
      res(videos);
    }
  }).then((v) => {
    videos = v;
    syncSpeedToVideo();
  });

  function getVideos() {
    let v;
    const videoElements = Array.prototype.slice.call(document.getElementsByTagName("video"));

    if (window.location.hostname === "www.bilibili.com") {
      const bPlayerCollection = document.getElementsByTagName("bwp-video");
      if (bPlayerCollection.length > 0) {
        videoElements.push(bPlayerCollection[0]);
      }
    }

    if (
      location.hostname === "www.reddit.com" &&
      window.location.pathname.match(/\/r\/\S+\/comments\/.+/)
    ) {
      const player = document.getElementsByTagName("shreddit-player");
      if (player.length > 0) {
        v = player[0].shadowRoot.querySelector("video");
        videoElements.push(v)
      }
    }

/*     if (location.hostname === "www.youtube.com") {
      if (v && v.className.indexOf("video-stream") === -1) {
        notify('视频正在初始化,请稍候')
        return null
      }
    } */

    return videoElements;
  }

  function formatSpeed(s) {
    s = parseFloat(s.toFixed(2));
    if (s > 10) {
      return 10;
    }
    if (s <= 0.1) {
      return 0.1;
    }
    return s;
  }

  window.addEventListener("keypress", function (e) {
    console.log(document.getElementsByTagName('video'))
    const activeElement = this.document.activeElement;
    if (
      activeElement.tagName.toLowerCase() === "input" ||
      activeElement.contentEditable === true ||
      activeElement.tagName.toLowerCase() === "textarea"
    ) {
      return;
    }
    const keyList = ['z','x','c','v']
    const key = e.key.toLowerCase()
    if (keyList.indexOf(key)===-1) {
      return
    }
    // 获取video元素
    videos = getVideos();
    if (videos.length===0) {
      return;
    }
    // **


    if (e.key.toLowerCase() === "c") {
      if (this.location.hostname === "www.youtube.com") {
        return;
      }
      speed = speed + step;

      changeSpeed(videos, formatSpeed(speed));
    }
    // youtube单独处理。
    if (
      this.location.hostname === "www.youtube.com" &&
      e.key.toLowerCase() === "v"
    ) {
      speed = speed + step;
      changeSpeed(videos, formatSpeed(speed));
    }
    if (e.key.toLowerCase() === "x") {
      speed = speed - step;
      changeSpeed(videos, formatSpeed(speed));
    }
    if (e.key.toLowerCase() === "z") {
      speed = 1;
      changeSpeed(videos, formatSpeed(speed));
    }
  });

  window.onload = function () {
    const videoElements = document.getElementsByTagName("video");
    if (videoElements.length === 0) return;

    videos = videoElements[0];
  };

  function changeSpeed(videos, speed) {

    videos.forEach((video)=>{
      video.playbackRate = speed;
      localStorage.setItem(SPEED_KEY, speed);
  
      video.removeEventListener("playing", syncSpeedToVideo);
      video.removeEventListener("play", syncSpeedToVideo);
  
      video.addEventListener("playing", syncSpeedToVideo);
      video.addEventListener("play", syncSpeedToVideo);
  
      notify(video,video.playbackRate);
    })


  }

  function syncSpeedToVideo() {
    videos.forEach((video)=>{
      setTimeout(() => {
        video.playbackRate = formatSpeed(speed);
      }, 1);
    })
  }

/*   function handleVideo(video) {
    if (isRememberSpeed) {
      video.playbackRate = speed;
    } else {
      speed = video.playbackRate;
    }
    addRememberMenu();  

    video.addEventListener("ratechange", function () {
      speed = video.playbackRate;
    });
  } */

  function notify(video,msg) {
    const className = "edbe85b469d47a8833b84e259864e33";
    const box = document.createElement("div");
    box.className = className;
    box.style.background = "#333";
    box.style.color = "#fff";
    box.style.padding = "8px 20px";
    box.style.position = "fixed";
    box.style.margin = "auto";
    box.style.left = "50%";
    box.style.top = "60px";
    box.style.transform = "translateX(-50%)";
    box.style.borderRadius = "5px";
    box.style.zIndex = "10000";
    box.style.fontSize = "16px";
    box.innerHTML = msg;
    const oldBox = document.querySelectorAll("." + className);
    if (oldBox.length) {
      oldBox.forEach((b) => {
        b.remove();
      });
    }
    if (video && document.fullscreenElement) {
      video.parentElement.appendChild(box);
    } else {
      document.body.appendChild(box);
    }
    setTimeout(() => {
      box.remove();
    }, 2000);
  }
})();