Udemy Speed Fix - UI Sync

Fixes Udemy bug: if UI shows 2x speed, ensure video plays at 2x after lecture change.

  1. // ==UserScript==
  2. // @name Udemy Speed Fix - UI Sync
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description Fixes Udemy bug: if UI shows 2x speed, ensure video plays at 2x after lecture change.
  6. // @author Ameer Jamal
  7. // @match https://*.udemy.com/course/*/learn/lecture/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=udemy.com
  9. // @grant none
  10. // @run-at document-idle
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. // === CONFIG ===
  17. const ACTION_DELAY_MS = 1000;
  18.  
  19. // === STATE ===
  20. let initialLoadCheckDone = false;
  21.  
  22. function log(message) {
  23. console.log("[Udemy Speed Fix v1.1] " + message);
  24. }
  25.  
  26. function extractSpeedFromElement(el) {
  27. if (!el) return null;
  28. const text = el.textContent.trim();
  29. const match = text.match(/^([0-9]\.?[0-9]*)x$/);
  30. if (match) {
  31. const value = parseFloat(match[1]);
  32. return isNaN(value) ? null : { text, value };
  33. }
  34. return null;
  35. }
  36.  
  37. function findSpeedFromButton() {
  38. const speedButton = document.querySelector('button[data-purpose="playback-rate-button"]');
  39. if (!speedButton) return null;
  40.  
  41. const innerSpan = speedButton.querySelector('span');
  42. return extractSpeedFromElement(innerSpan) || extractSpeedFromElement(speedButton);
  43. }
  44.  
  45. function findSpeedFromControlBars() {
  46. const controlBarSelectors = [
  47. 'div[class*="control-bar--control-bar--"]',
  48. 'div[data-purpose="video-controls"]'
  49. ];
  50. for (const selector of controlBarSelectors) {
  51. const bar = document.querySelector(selector);
  52. if (!bar) continue;
  53. const buttons = bar.querySelectorAll('button');
  54. for (const btn of buttons) {
  55. const info = extractSpeedFromElement(btn);
  56. if (info) return info;
  57. }
  58. }
  59. return null;
  60. }
  61.  
  62. function getCurrentDisplayedSpeedInfo() {
  63. const fromButton = findSpeedFromButton();
  64. if (fromButton) {
  65. log(`Found speed via playback-rate-button: ${fromButton.text}`);
  66. return fromButton;
  67. }
  68. const fromBar = findSpeedFromControlBars();
  69. if (fromBar) {
  70. log(`Found speed via control bar: ${fromBar.text}`);
  71. return fromBar;
  72. }
  73. log("Could not find current speed display element.");
  74. return null;
  75. }
  76.  
  77. function applySpeedFix() {
  78. log("Attempting to apply speed fix...");
  79. const videoElement = document.querySelector('video');
  80.  
  81. if (!videoElement) {
  82. log("No video element found on the page.");
  83. return;
  84. }
  85.  
  86. const displayedSpeedInfo = getCurrentDisplayedSpeedInfo();
  87.  
  88. if (displayedSpeedInfo && typeof displayedSpeedInfo.value === 'number' && !isNaN(displayedSpeedInfo.value)) {
  89. if (videoElement.playbackRate !== displayedSpeedInfo.value) {
  90. log(`Mismatch! UI shows ${displayedSpeedInfo.text}, video playbackRate is ${videoElement.playbackRate}. Setting to ${displayedSpeedInfo.value}.`);
  91. videoElement.playbackRate = displayedSpeedInfo.value;
  92. } else {
  93. log(`UI shows ${displayedSpeedInfo.text}, and video playbackRate is already correct.`);
  94. }
  95. } else {
  96. log("Could not determine a valid displayed speed from UI.");
  97. }
  98. }
  99.  
  100. function observeUrlChange(callback) {
  101. let lastUrl = location.href;
  102. new MutationObserver(() => {
  103. const currentUrl = location.href;
  104. if (currentUrl !== lastUrl) {
  105. lastUrl = currentUrl;
  106. callback();
  107. }
  108. }).observe(document, { subtree: true, childList: true });
  109. }
  110.  
  111. function runInitialSpeedFix() {
  112. if (document.readyState === 'complete') {
  113. if (!initialLoadCheckDone) {
  114. log("Document complete. Performing initial speed check.");
  115. setTimeout(applySpeedFix, ACTION_DELAY_MS);
  116. initialLoadCheckDone = true;
  117. }
  118. } else {
  119. setTimeout(runInitialSpeedFix, 500);
  120. }
  121. }
  122.  
  123. observeUrlChange(() => {
  124. log(`URL changed to ${window.location.href}`);
  125. setTimeout(applySpeedFix, ACTION_DELAY_MS);
  126. });
  127.  
  128. log("Script loaded. Monitoring for SPA URL changes.");
  129. runInitialSpeedFix();
  130. })();

QingJ © 2025

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