Shift Space Toggle Youtube CE Element

This is to toggle annoying Youtube CE Element near the end of the video

  1. // ==UserScript==
  2. // @name Shift Space Toggle Youtube CE Element
  3. // @name:en Shift Space Toggle Youtube CE Element
  4. // @name:ja Shift Space Toggle Youtube CE Element
  5. // @name:zh-TW Shift Space Toggle Youtube CE Element
  6. // @name:zh-CN Shift Space Toggle Youtube CE Element
  7. // @name Shift Space Toggle Youtube CE Element
  8. // @namespace http://tampermonkey.net/
  9. // @version 0.5.1
  10. // @description This is to toggle annoying Youtube CE Element near the end of the video
  11. // @description:en This is to toggle annoying Youtube CE Element near the end of the video
  12. // @description:ja これは、ビデオの終わり近くにある迷惑な Youtube CE 要素を切り替えるためのものです。
  13. // @description:zh-TW 這是為了切換在影片結尾附近煩人的 Youtube CE 元素
  14. // @description:zh-CN 這是為了切換在影片結尾附近煩人的 Youtube CE 元素
  15. // @author CY Fung
  16. // @match https://www.youtube.com/*
  17. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  18. // @grant none
  19. // @license MIT
  20. // ==/UserScript==
  21.  
  22. /* jshint esversion:6 */
  23. (function() {
  24. 'use strict';
  25.  
  26. let videoElm = null;
  27. const ceElems = [];
  28. let isPassiveAvailable = null
  29.  
  30. let earliestShown = -1;
  31. let endTime = -1;
  32.  
  33. const WeakRef = window?.WeakRef;
  34.  
  35. let videoReady = false
  36.  
  37. let lastPress = -1
  38. const allowList = [
  39. 'DIV', 'SPAN', 'BODY', 'HTML', 'VIDEO', 'A',
  40. 'YTD-PLAYER', 'YTD-WATCH-FLEXY', 'YTD-PAGE-MANAGER', 'YTD-MINIPLAYER'
  41. ];
  42.  
  43. function cacheCEElems() {
  44.  
  45. const m = document.querySelectorAll('ytd-player#ytd-player .ytp-ce-element');
  46. if (m.length === 0) return false;
  47.  
  48. ceElems.length = 0;
  49. for (const elm of m) {
  50. ceElems.push(new WeakRef(elm))
  51. }
  52.  
  53. return true;
  54.  
  55. }
  56.  
  57. function videoTimeUpdate(evt) {
  58.  
  59. //passive = true
  60. //capture = false
  61.  
  62. if (!videoReady) {
  63. if (evt?.target?.matches('ytd-player#ytd-player video')) {
  64. videoReady = true
  65. if (cacheCEElems() === false) setTimeout(cacheCEElems, 180);
  66. }
  67. }
  68.  
  69. if (ceElems.length === 0) return;
  70.  
  71. const video = evt.target;
  72. const anyShown = ceElems.some((elmRef) => elmRef.deref()?.classList?.contains('ytp-ce-element-show') === true)
  73. //console.log(135, anyShown, video.currentTime)
  74. if (anyShown) {
  75. if (earliestShown > 0 && -earliestShown < -video.currentTime) {
  76. earliestShown = video.currentTime
  77. endTime = video.duration
  78. } else if (earliestShown < 0) {
  79. videoElm = new WeakRef(video);
  80. earliestShown = video.currentTime
  81. endTime = video.duration
  82. }
  83. }
  84.  
  85.  
  86. }
  87.  
  88. function initialForVideoDetection(evt) {
  89.  
  90. //passive = true
  91. //capture = true
  92.  
  93. const video = evt?.target;
  94. if (video?.nodeName === 'VIDEO' && typeof video.src == 'string') {} else return;
  95.  
  96. videoReady = false;
  97. ceElems.length = 0;
  98.  
  99. videoElm = null;
  100. earliestShown = -1;
  101. endTime = -1;
  102.  
  103.  
  104. new Promise(function() {
  105.  
  106.  
  107. if (video.hasAttribute('usr-video-cem')) return;
  108. video.setAttribute('usr-video-cem', '');
  109.  
  110. if (isPassiveAvailable === null) isPassiveAvailable = checkPassive();
  111. video.addEventListener('timeupdate', videoTimeUpdate, optCapture(true, false));
  112.  
  113. })
  114.  
  115. }
  116.  
  117. function pageKeyDownfunction(evt) {
  118. //passive = false
  119. //capture = true
  120.  
  121. if (evt.code === 'Space' && evt.shiftKey) {
  122.  
  123. if (!allowList.includes(evt.target.nodeName)) return;
  124.  
  125. if (endTime > earliestShown && earliestShown > 0) {
  126.  
  127. let video = videoElm?.deref();
  128.  
  129. if (!video) return;
  130.  
  131. let p = (video.currentTime - earliestShown) / (endTime - earliestShown);
  132. if (p >= 0 && p <= 1) {
  133. evt.preventDefault();
  134. evt.stopPropagation();
  135. evt.stopImmediatePropagation();
  136.  
  137. for (const ceElem of ceElems) {
  138. ceElem?.deref()?.classList.toggle('ytp-ce-element-show');
  139. }
  140. }
  141.  
  142. }
  143.  
  144. }
  145. }
  146.  
  147. function checkPassive() {
  148.  
  149. let passiveSupported = false;
  150.  
  151. try {
  152. const options = {
  153. get passive() {
  154. passiveSupported = true;
  155. return false;
  156. }
  157. };
  158.  
  159. window.addEventListener("test", null, options);
  160. window.removeEventListener("test", null, options);
  161. } catch (err) {
  162. passiveSupported = false;
  163. }
  164.  
  165. return passiveSupported
  166.  
  167. }
  168.  
  169. const optCapture = (passive, capture) => isPassiveAvailable ? { passive, capture } : capture;
  170.  
  171. new Promise(function() {
  172.  
  173. if (isPassiveAvailable === null) isPassiveAvailable = checkPassive();
  174. document.addEventListener('canplay', initialForVideoDetection, optCapture(true, true));
  175.  
  176. })
  177.  
  178. document.addEventListener('keydown', pageKeyDownfunction, true)
  179.  
  180. //ytp-ce-video ytp-ce-top-left-quad ytp-ce-size-853 ytp-ce-element-show
  181.  
  182. // Your code here...
  183. })();