视频加速器+音量设置

更强大地识别网页中的视频,在右下角显示倍速操作界面,可隐藏为一个淡色圆形图标并显示倍速数字,显示操作界面时圆形图标隐藏。同时自动将音量设置为 30%。

  1. // ==UserScript==
  2. // @name 视频加速器+音量设置
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.2
  5. // @description 更强大地识别网页中的视频,在右下角显示倍速操作界面,可隐藏为一个淡色圆形图标并显示倍速数字,显示操作界面时圆形图标隐藏。同时自动将音量设置为 30%。
  6. // @license MIT
  7. // @author 失辛向南
  8. // @match *://*/*
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function () {
  13. 'use strict';
  14.  
  15. let speedControlContainer;
  16. let speedSelect;
  17. let isVisible = false;
  18. let iconContainer;
  19.  
  20. function createSpeedControlUI() {
  21. // 创建倍速控制界面容器
  22. speedControlContainer = document.createElement('div');
  23. speedControlContainer.style.position = 'fixed';
  24. speedControlContainer.style.bottom = '20px';
  25. speedControlContainer.style.right = '20px';
  26. speedControlContainer.style.backgroundColor = 'rgba(30, 30, 30, 0.8)';
  27. speedControlContainer.style.padding = '15px';
  28. speedControlContainer.style.borderRadius = '10px';
  29. speedControlContainer.style.color = '#fff';
  30. speedControlContainer.style.zIndex = '9999';
  31. speedControlContainer.style.display = 'none';
  32.  
  33. // 创建倍速标签
  34. const speedLabel = document.createElement('span');
  35. speedLabel.textContent = '播放速度:';
  36. speedControlContainer.appendChild(speedLabel);
  37.  
  38. // 创建下拉选择框
  39. speedSelect = document.createElement('select');
  40. speedSelect.classList.add('custom-speed-select');
  41. speedSelect.style.backgroundColor = 'rgba(50, 50, 50, 0.8)';
  42. speedSelect.style.color = '#fff';
  43. speedSelect.style.border = 'none';
  44. speedSelect.style.outline = 'none';
  45. speedSelect.style.appearance = 'none';
  46. speedSelect.style.padding = '5px 10px';
  47. speedSelect.style.borderRadius = '5px';
  48. for (let i = 0.25; i <= 5; i += 0.25) {
  49. const option = document.createElement('option');
  50. option.value = i;
  51. option.textContent = i + 'x';
  52. option.style.backgroundColor = 'rgba(50, 50, 50, 0.8)';
  53. option.style.color = '#fff';
  54. speedSelect.appendChild(option);
  55. }
  56. speedSelect.value = '1';
  57. speedControlContainer.appendChild(speedSelect);
  58.  
  59. document.body.appendChild(speedControlContainer);
  60.  
  61. speedSelect.addEventListener('change', updateVideoSpeeds);
  62.  
  63. // 创建图标容器
  64. iconContainer = document.createElement('div');
  65. iconContainer.style.position = 'fixed';
  66. iconContainer.style.bottom = '20px';
  67. iconContainer.style.right = '20px';
  68. iconContainer.style.width = '40px';
  69. iconContainer.style.height = '40px';
  70. iconContainer.style.backgroundColor = 'rgba(220, 220, 220, 0.6)';
  71. iconContainer.style.borderRadius = '50%';
  72. iconContainer.style.textAlign = 'center';
  73. iconContainer.style.lineHeight = '40px';
  74. iconContainer.style.cursor = 'pointer';
  75. iconContainer.style.zIndex = '9999';
  76. document.body.appendChild(iconContainer);
  77.  
  78. iconContainer.addEventListener('click', toggleVisibility);
  79.  
  80. document.addEventListener('mousemove', handleMouseMove);
  81.  
  82. const style = document.createElement('style');
  83. style.textContent = `
  84. .custom-speed-select option {
  85. color: #ffffff;
  86. }
  87. .custom-speed-select {
  88. -webkit-appearance: none;
  89. -moz-appearance: none;
  90. appearance: none;
  91. background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" fill="white"><polygon points="0,0 10,0 5,5"/></svg>');
  92. background-repeat: no-repeat;
  93. background-position: right 5px center;
  94. }
  95. `;
  96. document.head.appendChild(style);
  97. }
  98.  
  99. function updateVideoSpeeds() {
  100. const videos = findAllVideoElements();
  101. for (let video of videos) {
  102. video.playbackRate = parseFloat(speedSelect.value);
  103. video.volume = 0.3; // 设置音量为 30%
  104. }
  105. updateIconText();
  106. }
  107.  
  108. function checkForVideos() {
  109. const videos = findAllVideoElements();
  110. if (videos.length > 0) {
  111. if (!speedControlContainer) {
  112. createSpeedControlUI();
  113. }
  114. updateVideoSpeeds();
  115. } else {
  116. if (speedControlContainer) {
  117. speedControlContainer.remove();
  118. speedControlContainer = null;
  119. speedSelect = null;
  120. iconContainer.remove();
  121. iconContainer = null;
  122. }
  123. }
  124. }
  125.  
  126. function isVideoPage() {
  127. const videoElements = findAllVideoElements();
  128. return videoElements.length > 0;
  129. }
  130.  
  131. function handleMouseMove(event) {
  132. if (!isVisible && isInsideIcon(event.clientX, event.clientY)) {
  133. showContainer();
  134. } else if (isVisible &&!isInsideContainer(event.clientX, event.clientY)) {
  135. hideContainer();
  136. }
  137. }
  138.  
  139. function isInsideIcon(x, y) {
  140. const iconRect = iconContainer.getBoundingClientRect();
  141. return x >= iconRect.left && x <= iconRect.right && y >= iconRect.top && y <= iconRect.bottom;
  142. }
  143.  
  144. function isInsideContainer(x, y) {
  145. const containerRect = speedControlContainer.getBoundingClientRect();
  146. return x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
  147. }
  148.  
  149. function showContainer() {
  150. speedControlContainer.style.display = 'block';
  151. iconContainer.style.display = 'none';
  152. isVisible = true;
  153. }
  154.  
  155. function hideContainer() {
  156. speedControlContainer.style.display = 'none';
  157. iconContainer.style.display = 'block';
  158. isVisible = false;
  159. }
  160.  
  161. function toggleVisibility() {
  162. if (isVisible) {
  163. hideContainer();
  164. } else {
  165. showContainer();
  166. }
  167. }
  168.  
  169. function updateIconText() {
  170. iconContainer.textContent = speedSelect.value + 'x';
  171. }
  172.  
  173. function findAllVideoElements() {
  174. const videoTags = Array.from(document.getElementsByTagName('video'));
  175. const iframeVideos = Array.from(document.querySelectorAll('iframe[src*=".mp4"], iframe[src*=".webm"], iframe[src*=".ogg"]'))
  176. .map(element => {
  177. if (element.tagName === 'IFRAME') {
  178. return element.contentDocument? element.contentDocument.getElementsByTagName('video')[0] : null;
  179. } else {
  180. return null;
  181. }
  182. })
  183. .filter(video => video!== null);
  184. const customVideoElements = Array.from(document.querySelectorAll('[data-video]'))
  185. .map(element => {
  186. const videoSrc = element.getAttribute('data-video');
  187. if (videoSrc && (videoSrc.endsWith('.mp4') || videoSrc.endsWith('.webm') || videoSrc.endsWith('.ogg'))) {
  188. const video = document.createElement('video');
  189. video.src = videoSrc;
  190. return video;
  191. } else {
  192. return null;
  193. }
  194. })
  195. .filter(video => video!== null);
  196. const potentialVideoElements = Array.from(document.querySelectorAll('*'))
  197. .filter(element => {
  198. const backgroundImage = window.getComputedStyle(element).backgroundImage;
  199. return backgroundImage && (backgroundImage.includes('.mp4') || backgroundImage.includes('.webm') || backgroundImage.includes('.ogg'));
  200. })
  201. .map(element => {
  202. const video = document.createElement('video');
  203. const backgroundImage = window.getComputedStyle(element).backgroundImage;
  204. const videoSrc = backgroundImage.match(/url\((.*?)\)/)[1];
  205. video.src = videoSrc;
  206. return video;
  207. })
  208. .filter(video => video!== null);
  209. return videoTags.concat(iframeVideos).concat(customVideoElements).concat(potentialVideoElements);
  210. }
  211.  
  212. window.addEventListener('load', () => {
  213. if (isVideoPage()) {
  214. checkForVideos();
  215. }
  216. });
  217. window.addEventListener('DOMContentLoaded', () => {
  218. if (isVideoPage()) {
  219. checkForVideos();
  220. }
  221. });
  222. })();

QingJ © 2025

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