Udemy Speed Fix - UI Sync

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name         Udemy Speed Fix - UI Sync
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Fixes Udemy bug: if UI shows 2x speed, ensure video plays at 2x after lecture change.
// @author       Ameer Jamal
// @match        https://*.udemy.com/course/*/learn/lecture/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=udemy.com
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
    'use strict';

    // === CONFIG ===
    const ACTION_DELAY_MS = 1000;

    // === STATE ===
    let initialLoadCheckDone = false;

    function log(message) {
        console.log("[Udemy Speed Fix v1.1] " + message);
    }

    function extractSpeedFromElement(el) {
        if (!el) return null;
        const text = el.textContent.trim();
        const match = text.match(/^([0-9]\.?[0-9]*)x$/);
        if (match) {
            const value = parseFloat(match[1]);
            return isNaN(value) ? null : { text, value };
        }
        return null;
    }

    function findSpeedFromButton() {
        const speedButton = document.querySelector('button[data-purpose="playback-rate-button"]');
        if (!speedButton) return null;

        const innerSpan = speedButton.querySelector('span');
        return extractSpeedFromElement(innerSpan) || extractSpeedFromElement(speedButton);
    }

    function findSpeedFromControlBars() {
        const controlBarSelectors = [
            'div[class*="control-bar--control-bar--"]',
            'div[data-purpose="video-controls"]'
        ];
        for (const selector of controlBarSelectors) {
            const bar = document.querySelector(selector);
            if (!bar) continue;
            const buttons = bar.querySelectorAll('button');
            for (const btn of buttons) {
                const info = extractSpeedFromElement(btn);
                if (info) return info;
            }
        }
        return null;
    }

    function getCurrentDisplayedSpeedInfo() {
        const fromButton = findSpeedFromButton();
        if (fromButton) {
            log(`Found speed via playback-rate-button: ${fromButton.text}`);
            return fromButton;
        }
        const fromBar = findSpeedFromControlBars();
        if (fromBar) {
            log(`Found speed via control bar: ${fromBar.text}`);
            return fromBar;
        }
        log("Could not find current speed display element.");
        return null;
    }

    function applySpeedFix() {
        log("Attempting to apply speed fix...");
        const videoElement = document.querySelector('video');

        if (!videoElement) {
            log("No video element found on the page.");
            return;
        }

        const displayedSpeedInfo = getCurrentDisplayedSpeedInfo();

        if (displayedSpeedInfo && typeof displayedSpeedInfo.value === 'number' && !isNaN(displayedSpeedInfo.value)) {
            if (videoElement.playbackRate !== displayedSpeedInfo.value) {
                log(`Mismatch! UI shows ${displayedSpeedInfo.text}, video playbackRate is ${videoElement.playbackRate}. Setting to ${displayedSpeedInfo.value}.`);
                videoElement.playbackRate = displayedSpeedInfo.value;
            } else {
                log(`UI shows ${displayedSpeedInfo.text}, and video playbackRate is already correct.`);
            }
        } else {
            log("Could not determine a valid displayed speed from UI.");
        }
    }

    function observeUrlChange(callback) {
        let lastUrl = location.href;
        new MutationObserver(() => {
            const currentUrl = location.href;
            if (currentUrl !== lastUrl) {
                lastUrl = currentUrl;
                callback();
            }
        }).observe(document, { subtree: true, childList: true });
    }

    function runInitialSpeedFix() {
        if (document.readyState === 'complete') {
            if (!initialLoadCheckDone) {
                log("Document complete. Performing initial speed check.");
                setTimeout(applySpeedFix, ACTION_DELAY_MS);
                initialLoadCheckDone = true;
            }
        } else {
            setTimeout(runInitialSpeedFix, 500);
        }
    }

    observeUrlChange(() => {
        log(`URL changed to ${window.location.href}`);
        setTimeout(applySpeedFix, ACTION_DELAY_MS);
    });

    log("Script loaded. Monitoring for SPA URL changes.");
    runInitialSpeedFix();
})();