HTML5快速鍵hotkeys

HTML5快速鍵控制跳秒+速度+寬高比+截圖

当前为 2024-04-29 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        HTML5快速鍵hotkeys
// @namespace   https://greasyfork.org/zh-TW/users/4839-leadra
// @version     1.2.6
// @license     AGPLv3
// @author      jcunews,leadra
// @description HTML5快速鍵控制跳秒+速度+寬高比+截圖
// @match       https://*.youtube.com/*
// @match       https://ani.gamer.com.tw/*
// @match       https://web.telegram.org/k/*
// @grant       none
// @run-at      document-start
// @icon        https://www.youtube.com/favicon.ico
// ==/UserScript==
// @match       *://*/*

((eleOSD, osdTimer) => {

  //速度倍率
  var incrementUnit = 0.2;
  //變更播放速率時螢幕右下提示 (OSD) 的持續時間(以毫秒為單位)。 設定為零或更少以停用。
  var osdTimeout = 600;
  //截圖格式jpeg, png
  var imageFormat = "jpeg";

 //鍵盤快速鍵。
   // 每個鍵名可以是該鍵產生的字元(例如 'a'、'4'、'*' 等),
   // 例如 'Digit2'、'BracketLeft' 等兩種類型都區分大小寫。
   //修飾符 = "C"、"S"和"A"的任意組合,用於 Ctrl、Shift 和 Alt 鍵。
  var keys = [
    /*{ //0 to 9: 跳到 0%,10%,20%,...90%
      key: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], modifiers: "",
      func: (ele, key, keyIndex) => ele.currentTime = keyIndex / 10 * ele.duration
    },*/
    { //shift+0 to shift+9: 跳到 5%,15%,25%,...95%
      key: [")", "!", "@", "#", "$", "%", "^", "&", "*", "("], modifiers: "S",
      func: (ele, key, keyIndex) => ele.currentTime = (keyIndex + 0.5) / 10 * ele.duration
    },
    { //Alt+1 至 Alt+5 = 將音量改為 20%、40%、60$、80%、100%
      key: ["1", "2", "3", "4", "5"], modifiers: "A",
      func: (ele, key, keyIndex)  => updAudioVolume(ele, (parseInt(key) * 2) / 10)
    },
    //前進後退
    {
      key: "a", modifiers: "",
      func: (ele, key) => ele.currentTime -= 1
    },
     {
      key: "A", modifiers: "",
      func: (ele, key) => ele.currentTime -= 1
    },
    {
      key: "d", modifiers: "",
      func: (ele, key) => ele.currentTime += 1
    },
    {
      key: "D", modifiers: "",
      func: (ele, key) => ele.currentTime += 1
    },
    {
      key: "A", modifiers: "SA",
      func: (ele, key) => ele.currentTime -= 99999
    },
    {
      key: "a", modifiers: "SA",
      func: (ele, key) => ele.currentTime -= 99999
    },
    {
      key: "D", modifiers: "SA",
      func: (ele, key) => ele.currentTime += 99999
    },
    {
      key: "d", modifiers: "SA",
      func: (ele, key) => ele.currentTime += 99999
    },
    {
      key: "ArrowLeft", modifiers: "",
      func: (ele, key) => ele.currentTime -= 3
    },
    {
      key: "ArrowRight", modifiers: "",
      func: (ele, key) => ele.currentTime += 3
    },
    {
      key: "ArrowLeft", modifiers: "C",
      func: (ele, key) => ele.currentTime -= 30
    },
    {
      key: "ArrowRight", modifiers: "C",
      func: (ele, key) => ele.currentTime += 30
    },
    {
      key: "A", modifiers: "S",
      func: (ele, key) => ele.currentTime -= 30
    },
    {
      key: "a", modifiers: "S",
      func: (ele, key) => ele.currentTime -= 30
    },
    {
      key: "D", modifiers: "S",
      func: (ele, key) => ele.currentTime += 30
    },
    {
      key: "d", modifiers: "S",
      func: (ele, key) => ele.currentTime += 30
    },
    {
      key: ",", modifiers: "C",
      func: (ele, key) => ele.currentTime -= 1/30
    },
    {
      key: ".", modifiers: "C",
      func: (ele, key) => ele.currentTime += 1/30
    },
    //速度-
    {
      key: "z", modifiers: "",
      func: (ele, key) => {
        key = ele.playbackRate - incrementUnit;
        if (key < 0.1) {
          key = 0.1;
        } else if ((key < 1) && (ele.playbackRate > 1)) key = 1;
        updVideoSpeed(ele, key);
      }
    },
    {
      key: "Z", modifiers: "",
      func: (ele, key) => {
        key = ele.playbackRate - incrementUnit;
        if (key < 0.1) {
          key = 0.1;
        } else if ((key < 1) && (ele.playbackRate > 1)) key = 1;
        updVideoSpeed(ele, key);
      }
    },
    {
      key: "-", modifiers: "",
      func: (ele, key) => {
        key = ele.playbackRate - incrementUnit;
        if (key < 0.1) {
          key = 0.1;
        } else if ((key < 1) && (ele.playbackRate > 1)) key = 1;
        updVideoSpeed(ele, key);
      }
    },
    //速度+
    {
      key: "x", modifiers: "",
      func: (ele, key) => {
        key = ele.playbackRate + incrementUnit;
        if (key > 16) {
          key = 16;
        } else if ((key > 1) && (ele.playbackRate < 1)) key = 1;
        updVideoSpeed(ele, key);
      }
    },
    {
      key: "X", modifiers: "",
      func: (ele, key) => {
        key = ele.playbackRate + incrementUnit;
        if (key > 16) {
          key = 16;
        } else if ((key > 1) && (ele.playbackRate < 1)) key = 1;
        updVideoSpeed(ele, key);
      }
    },
    {
      key: "+", modifiers: "",
      func: (ele, key) => {
        key = ele.playbackRate + incrementUnit;
        if (key > 16) {
          key = 16;
        } else if ((key > 1) && (ele.playbackRate < 1)) key = 1;
        updVideoSpeed(ele, key);
      }
    },
    {//速度+1X
      key: "X", modifiers: "S",
      func: (ele, key) => {
        key = ele.playbackRate + incrementUnit;
        if (key > 16) {
          key = 16;
        } else if ((key > 1) && (ele.playbackRate < 1)) key = 1;
        updVideoSpeed(ele, 0.8+key);
      }
    },
    {
      key: "x", modifiers: "S",
      func: (ele, key) => {
        key = ele.playbackRate + incrementUnit;
        if (key > 16) {
          key = 16;
        } else if ((key > 1) && (ele.playbackRate < 1)) key = 1;
        updVideoSpeed(ele, 0.8+key);
      }
    },
    //速度復原1x
    {
      key: "c", modifiers: "",
      func: (ele, key) => {updVideoSpeed(ele, 1)}
    },
    {
      key: "C", modifiers: "",
      func: (ele, key) => {updVideoSpeed(ele, 1)}
    },
    {
      key: "Z", modifiers: "S",
      func: (ele, key) => {updVideoSpeed(ele, 1)}
    },
    {
      key: "z", modifiers: "S",
      func: (ele, key) => {updVideoSpeed(ele, 1)}
    },
    {
      key: "*", modifiers: "",
      func: (ele, key) => {updVideoSpeed(ele, 1)}
    },
    { //ctrl+': 更改預設速度
      key: "'", modifiers: "C",
      func: (ele, key) => {
        if ((key = prompt("Enter media speed from 0.1 to 16 (inclusive).\ne.g.: 1 = Normal, 0.5 = Half, 2 = Double, 3 = Triple, etc.", ele.playbackRate)) === null) return;
        if (isNaN(key = parseFloat(key.trim()))) {
          alert("Input must be a number.");
          return;
        }
        updVideoSpeed(ele, (key = parseFloat(key.toFixed(1))) < 0.1 ? 0.1 : (key > 16 ? 16 : key));
      }
    },
    { //ctrl+\: 更改速度倍率
      key: "\\", modifiers: "C",
      func: (ele, key) => {
        if ((key = prompt("Enter unit of media speed increment/decrement from 0.1 to 4 (inclusive).", incrementUnit)) === null) return;
        if (!isNaN(key = parseFloat(key.trim()))) {
          incrementUnit = (key = parseFloat(key.toFixed(1))) < 0.1 ? 0.1 : (key > 4 ? 4 : key);
        } else alert("Input must be a number.");
      }
    },
    //截圖screenshot
    {
      key: "S", modifiers: "S", videoOnly: false,
      func: (ele, key) => screenshot()
    },
    //對於寬螢幕視窗ctrl+6.7.8
    {
      key: "6", modifiers: "C", videoOnly: true,
      func: (ele, key) => updVideoAspect("scaleX(1.3333)", "Widescreen")
    },
    {
      key: "7", modifiers: "C", videoOnly: true,
      func: (ele, key) => updVideoAspect("scaleY(1.3333)", "Letterbox")
    },
    {
      key: "8", modifiers: "C", videoOnly: true,
      func: (ele, key) => updVideoAspect("scaleX(0.75)", "TV")
    },
    //對於 4:3 視窗CTRL+SHIFT+6.7.8
    {
      key: "Digit6", modifiers: "CS", videoOnly: true,
      func: (ele, key) => updVideoAspect("scaleY(0.7168)", "Ultra Widescreen")
    },
    {
      key: "Digit7", modifiers: "CS", videoOnly: true,
      func: (ele, key) => updVideoAspect("scale(1.1666)", "Letterbox Half-Zoom")
    },
    {
      key: "Digit8", modifiers: "CS", videoOnly: true,
      func: (ele, key) => updVideoAspect("scaleY(0.5625)", "Widescreen On TV")
    },
    //CTRL+9 = 重置影片寬高比
    {
      key: "9", modifiers: "C", videoOnly: true,
      func: (ele, key) => updVideoAspect("", "Reset")
    }
  ];
  keys.forEach((k, s, m) => {
    s = k.modifiers.toUpperCase();
    k.modifiers = {ctrl: s.includes("C"), shift: s.includes("S"), alt: s.includes("A")};
  });

//截圖設定
 function screenshot(ele, cv) {
    if (ele = document.querySelector("video")) {
      cv = document.createElement("CANVAS");
      if (cv.width = ele.videoWidth) {
        cv.height = ele.videoHeight;
        cv.getContext("2d").drawImage(ele, 0, 0);
        ele = document.createElement("A");
        ele.href = cv.toDataURL("image/" + imageFormat);
        const VideoElement = document.querySelector('video');
        const CurrentTime = VideoElement.currentTime;
        const Hours = Math.floor(CurrentTime / 3600);
        const Minutes = Math.floor((CurrentTime % 3600) / 60);
        const Seconds = Math.floor(CurrentTime % 60);
        const FormattedTime = `${Hours.toString().padStart(2, '0')}${Minutes.toString().padStart(2, '0')}${Seconds.toString().padStart(2, '0')}`;
        ele.download = document.title + `-${FormattedTime}.${imageFormat === "jpeg" ? "jpg" : imageFormat}`;//
        ele.style.visibility = "hidden";
        document.body.appendChild(ele).click();
        ele.remove();
        return;
      } else {
        alert("The HTML5 video media has not been loaded yet.");
      }
    } else {
      alert("There is no HTML5 video on this page.");
    }
  };
  //提示框
  function showOSD(s) {
    if (osdTimeout < 0) return;
    if (eleOSD) {
      eleOSD.textContent = s;
    } else {
      eleOSD = document.createElement("DIV");
      eleOSD.style.cssText = "position:fixed;z-index:999999999;right:.5rem;bottom:.5rem;margin:0;padding:.2rem .5rem .1rem .5rem;width:auto;height:auto;font:normal 16pt/normal sans-serif;background:#444;color:#fff";
      eleOSD.textContent = s;
      document.body.appendChild(eleOSD);
    }
    clearTimeout(osdTimer);
    osdTimer = setTimeout(() => {
      eleOSD.remove();
      eleOSD = null;
    }, osdTimeout);
  }

  function stopEvent(ev) {
    ev.preventDefault();
    ev.stopPropagation();
    ev.stopImmediatePropagation();
  }
  //速度修改
  function updVideoSpeed(ele, spd, e) {
    ele.playbackRate = spd = parseFloat(spd.toFixed(1));
    showOSD("Speed " + spd + "x");
  }

  function updVideoAspect(asp, label, s) {
    if (!(s = document.getElementById("vidAspOvr"))) document.body.appendChild(s = document.createElement("STYLE")).id = "vidAspOvr";
    s.innerHTML = asp ? `video{transform:${asp}!important}` : "";
    showOSD("Ratio: " + label);
  }
  //音量修改
  function updAudioVolume(ele, vol, e) {
    if ((location.hostname === "www.youtube.com") && (e = ele.parentNode.parentNode).setVolume) {
      e.setVolume(vol * 100);
    } else ele.volume = vol;
    showOSD("Audio " + (vol * 100) + "%");
  }

  function isVisible(ele) {
    while (ele && ele.tagName) {
      if (getComputedStyle(ele).display === "none") return false;
      ele = ele.parentNode
    }
    return true
  }

  incrementUnit = parseFloat((incrementUnit < 0.1 ? 0.1 : (incrementUnit > 1 ? 1 : incrementUnit)).toFixed(1));
  addEventListener("keydown", function(ev, ele) {
    if ((!(ele = document.activeElement) || !((ele.contentEditable === "true") || ["BUTTON", "INPUT", "SELECT", "TEXTAREA"].includes(ele.tagName))) && (ele = document.querySelector("video,audio"))) {
      keys.some((k, a, i) => {
        a = Array.isArray(k.key);
        if (
          ((!a && ((k.key === ev.key) || (k.key === ev.code))) || (a && (((i = k.key.indexOf(ev.key)) >= 0) || ((i = k.key.indexOf(ev.code)) >= 0)))) &&
          (k.modifiers.ctrl === ev.ctrlKey) && (k.modifiers.shift === ev.shiftKey) && (k.modifiers.alt === ev.altKey) &&
          (!k.videoOnly || (ele.tagName === "VIDEO")) && (isVisible(ele) || (ele.tagName === "AUDIO"))
        ) {
          stopEvent(ev);
          k.func(ele, ev.key, a ? i : null);
          return true;
        }
      });
    }
  }, true);

})();