HTML5快速鍵hotkeys

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

2024-02-20 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name        HTML5快速鍵hotkeys
// @namespace   https://greatest.deepsurf.us/zh-TW/users/4839-leadra
// @version     1.1.1
// @license     AGPLv3
// @author      jcunews,leadra
// @description HTML5快速鍵控制跳秒+速度+寬高比+截圖
// @description:EN Adds keyboard shortcuts for controlling HTML5 media player. Seek media to N%. Rewind and fast fordward. Change media speed even beyond YouTube's speed limit. Change audio volume to N%. Change video aspect ratio for TV and letterbox content (for widescreen monitors).
// @match       https://www.youtube.com/*
// @match       https://ani.gamer.com.tw/*
// @match       https://anime1.me/*
!// @match       *://*/*
// @grant       none
// @run-at      document-start
// @icon         https://www.google.com/s2/favicons?domain=youtube.com
// ==/UserScript==

/*
鍵盤快速鍵:
CTRL+, = 快退 1/30 秒
CTRL+。 = 快進 1/30 秒
CTRL+左鍵 = 快退 30 秒
CTRL+右=前進 秒 30
a = 快退 分鐘 1
d = 分鐘 1
A = 快退秒 99999
D = 快進秒 99999
CTRL+/ = 快進分鐘 1.5
0 至 9 = 跳至 0%、10%、20%、...90%
SHIFT+0 至 SHIFT+9 = 跳至 5%、15%、25%、...95%
Alt+1 至 Alt+5 = 將音量改為 20%、40%、60$、80%、100%
[Z] = 速度降低 0.2 倍(預設)
[X] = 速度提高 0.2 倍(預設)
[C] =
CTRL+' = 更改預設速度
CTRL+\ = 更改速度倍率

對於寬螢幕視窗:
CTRL+6 = 變更寬螢幕內容的影片寬高比。 修正寬螢幕內容縮小為 4:3 電視格式的問題。
CTRL+7 = 更改內容的影片寬高比。 修正 4:3 內容拉伸為寬螢幕格式的問題。
CTRL+8 = 更改內容的影片寬高比。 修正 4:3 內容拉伸為寬螢幕格式的問題。

對於 4:3 視窗:
CTRL+SHIFT+6 = 變更超寬螢幕內容的影片寬高比。 修正壓縮為 4:3 電視格式的超寬螢幕內容。
CTRL+SHIFT+7 = 縮放 4:3 信箱內容以刪除頂部+底部邊框的一半,同時也刪除一點左側+右側的內容。
                      這也可用於在寬螢幕視窗上半縮放超寬螢幕內容。 即 CTRL+6 的半縮放。
CTRL+SHIFT+8 = 變更寬螢幕內容的影片寬高比。 修正壓縮為 4:3 電視格式的寬螢幕內容。

對於任何視窗:
CTRL+9 = 重置影片寬高比
*/

((eleOSD, osdTimer) => {

  //===預設===

  //速度倍率
  var incrementUnit = 0.2;

  //變更播放速率時螢幕右下提示 (OSD) 的持續時間(以毫秒為單位)。 設定為零或更少以停用。
  var osdTimeout = 3000;

 //鍵盤快速鍵。
   //鍵=鍵名。 如果是單一快捷方式,則為字串類型;如果有多個快捷方式,則為字串數組(對於單一函數多個快捷方式)。
   // 每個鍵名可以是該鍵產生的字元(例如 `a`、`4`、`*` 等),
   // 或鍵的程式碼名稱(例如 `Digit2`、`BracketLeft` 等)。 兩種類型都區分大小寫。
   // 當 SHIFT 修飾符與產生字元的鍵一起使用時,應使用鍵代碼名稱。
   // 可以在此處找到關鍵代碼名稱清單:https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values
   //修飾符 = “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: "S",
      func: (ele, key) => ele.currentTime -= 99999
    },
    {
      key: "ArrowLeft", modifiers: "C",
      func: (ele, key) => ele.currentTime -= 30
    },
    {
      key: ",", modifiers: "C",
      func: (ele, key) => ele.currentTime -= 1/30
    },
    {
      key: "d", modifiers: "",
      func: (ele, key) => ele.currentTime += 1
    },
    {
      key: "D", modifiers: "S",
      func: (ele, key) => ele.currentTime += 99999
    },
    {
      key: "ArrowRight", modifiers: "C",
      func: (ele, key) => ele.currentTime += 30
    },
    {
      key: ".", modifiers: "C",
      func: (ele, key) => ele.currentTime += 1/30
    },

    {
      key: "/", modifiers: "C",
      func: (ele, key) => ele.currentTime += 90
    },
    //速度-
    {
      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: "+", 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);
      }
    },
    //[C]: 速度復原1x
    {
      key: "c", modifiers: "",
      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: true,
      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")};
  });

  //=== 設定結束 ===

  //=== CONFIGURATION BEGIN ===
  var imageFormat = "jpeg"; //can be one of these: jpeg, png
  //=== CONFIGURATION END ===

 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);
        ele.download = (document.title) ;//+ (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) {
    if ((location.hostname === "www.youtube.com") && (e = ele.parentNode.parentNode).setPlaybackRate && (spd >= 0.25) && (spd <= 2)) {
      e.setPlaybackRate(spd = parseFloat(spd.toFixed(1)));
    } else 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);

})();