Miigon's Youtube script

useful enhancements for my own youtube enjoyments

  1. // ==UserScript==
  2. // @name Miigon's Youtube script
  3. // @namespace https://blog.miigon.net/
  4. // @version 0.6
  5. // @description useful enhancements for my own youtube enjoyments
  6. // @author Miigon
  7. // @match https://www.youtube.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. /* Long press right arrow key to speed up video playback */
  14. const RIGHT_ARROW_SPEEDUP_PRESS_TIME_MS = 500; // key hold time before speeding up
  15. const SPEED_UP_PLAYBACK_RATE = 3;
  16. /* Seeking in youtube shorts */
  17. const SHORTS_SEEK_SECS = 5;
  18.  
  19. (function() {
  20. 'use strict';
  21.  
  22. let mlog = (...args)=>{console.log("miigon's ytb script:", ...args)}
  23.  
  24. let right_pressed = false;
  25. let is_speeding_up = false;
  26. let speeding_timeout = -1; // timeout waiting before starting speeding up
  27. let last_playback_rate = 1;
  28.  
  29. let is_shorts = () => window.location.href.includes("/shorts/");
  30. let is_editing = () => document.activeElement.contentEditable == "true";
  31.  
  32. // youtube uses javascript to navigate around.
  33. // so cached_player is only valid if the page is still the same.
  34. let cached_player_href = null;
  35. let cached_player = null;
  36. let getPlayerElement = () => {
  37. if(window.location.href == cached_player_href && cached_player) {
  38. return cached_player;
  39. }
  40.  
  41. mlog("invalidate player element cache")
  42. cached_player = null;
  43. let ytd_player = [...document.getElementsByTagName("ytd-player")];
  44. if(is_shorts()) {
  45. cached_player = ytd_player.find(v=>v.className.indexOf("ytd-shorts")!=-1);
  46. } else { // normal full-length video
  47. cached_player = ytd_player.find(v=>
  48. v.className.indexOf("preview") == -1
  49. && v.className.indexOf("ytd-shorts") == -1
  50. );
  51. }
  52. if(cached_player != null) {
  53. cached_player_href = window.location.href;
  54. }
  55. return cached_player;
  56. }
  57. let getPlayer = () => getPlayerElement().getPlayer();
  58. let getPlayerVideoTag = () => getPlayerElement().getElementsByTagName("video")[0];
  59.  
  60.  
  61.  
  62. document.addEventListener("keydown", (e)=>{
  63. let used = false;
  64.  
  65. // skip hotkeys when editing comment etc.
  66. if(is_editing()) return;
  67.  
  68. if(e.key === "ArrowLeft") {
  69. // enables left seeking in shorts
  70. // ArrowRight is handled in keyup instead of keydown.
  71. if(is_shorts()){
  72. getPlayer().seekBy(-SHORTS_SEEK_SECS);
  73. }
  74. }
  75.  
  76. if(e.miigon_ignore) {
  77. return;
  78. }
  79.  
  80. if(e.key === "ArrowRight") {
  81. if(right_pressed == false){
  82. right_pressed = true;
  83. mlog("right pressed");
  84. speeding_timeout = setTimeout(()=>{
  85. speeding_timeout = -1;
  86. is_speeding_up = true;
  87. let v = getPlayerVideoTag();
  88. last_playback_rate = v.playbackRate;
  89. v.playbackRate = SPEED_UP_PLAYBACK_RATE;
  90. mlog("speeding up");
  91.  
  92. used = true;
  93. }, RIGHT_ARROW_SPEEDUP_PRESS_TIME_MS);
  94. }
  95. used = true;
  96. }
  97.  
  98. if(used){
  99. e.stopPropagation();
  100. e.preventDefault();
  101. }
  102. }, /*useCapture*/true);
  103.  
  104. document.addEventListener("keyup", (e)=>{
  105. let used = false;
  106.  
  107. // skip hotkeys when editing comment etc.
  108. if(is_editing()) return;
  109.  
  110. if(e.key === "ArrowRight") {
  111. right_pressed = false;
  112. if(speeding_timeout != -1) { // keyup before speeding up begins
  113. clearTimeout(speeding_timeout);
  114. speeding_timeout = -1;
  115.  
  116. if(is_shorts()){
  117. // shorts, so simulating a right arrow keypress wont work
  118. // tell the player to seek directly.
  119. // this enables right seeking in shorts
  120. getPlayer().seekBy(SHORTS_SEEK_SECS);
  121. } else { // normal video
  122. // dispatch a normal right arrow keypress event.
  123. let event = new KeyboardEvent("keydown", {keyCode: 39})
  124. event.miigon_ignore = true; // the script should not process this event
  125. document.dispatchEvent(event);
  126. }
  127. }
  128. if(is_speeding_up) { // keyup after speeding up begins
  129. is_speeding_up = false;
  130. mlog("stop speeding up");
  131. getPlayerVideoTag().playbackRate = last_playback_rate;
  132. used = true;
  133. }
  134. }
  135.  
  136. if(used){
  137. e.stopPropagation();
  138. e.preventDefault();
  139. }
  140. }, /*useCapture*/true);
  141. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址