OpenU Video Speed Enhancer

Increases the maximum playback speed of OpenU video player

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         OpenU Video Speed Enhancer
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Increases the maximum playback speed of OpenU video player
// @author       Claude 3.7 w/ Bar Borer
// @match        *://*.openu.ac.il/*
// @grant        none
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Function to override the AddSpeedControlLogic function
    function overrideSpeedControl() {
        // Make sure $openu exists
        if (typeof $openu === 'undefined' || !$openu) {
            console.log("Waiting for $openu to be defined...");
            setTimeout(overrideSpeedControl, 1000);
            return;
        }

        // Add CSS to fix overflow issues
        const styleElement = document.createElement('style');
        styleElement.textContent = `
            .openu-speed-menu {
                min-width: 120px !important;
                max-height: 400px !important;
                overflow-y: auto !important;
                overflow-x: hidden !important;
            }
            
            .openu-speed-menu:not([aria-expanded="true"]) {
                display: none !important;
            }
            
            .openu-speed-menu[aria-expanded="true"] {
                display: flex !important;
                flex-direction: column !important;
            }
            
            .openu-speed-content-item {
                text-align: center !important;
                width: 100% !important;
                padding: 8px 15px !important;
                font-size: 14px !important;
            }
        `;
        document.head.appendChild(styleElement);

        // Store the original function
        const originalAddSpeedControlLogic = $openu.AddSpeedControlLogic;

        // Override the function
        $openu.AddSpeedControlLogic = function(player) {
            if (document.querySelector('video') == null)
                return;

            // Change these values to adjust the speed options
            var minSpeed = 0.5;     // Original: 0.75
            var maxSpeed = 4.0;     // Changed from 5.0 to 4.0
            var speedStep = 0.25;   // Original: 0.25
            var defaultSpeed = 1;

            var setPlaySpeed = function(speed) {
                var i = parseFloat(speed);
                if (player) {
                    if (player.getPlaybackRate()) {
                        if (i < minSpeed || i > maxSpeed)
                            return !1;
                        return player.setPlaybackRate(i);
                    }
                }
            };

            var PlayerSpeedInit = function() {
                var speedList = $('<div/>').addClass('jw-reset openu-menu openu-speed-menu').attr('role', 'menu').attr('aria-expanded', 'false').attr('aria-label', $openu.str('speedmenu'));
                var speeds = [];
                var currentSpeed = minSpeed;
                var btn = $('<div/>').attr('role', "button").attr('tabindex', 0).attr('aria-haspopup', "true").addClass('jw-icon jw-icon-inline jw-button-color jw-reset openu-speed-btn').attr('aria-label', $openu.str('speedmenu')).on('click', function() {
                    var m = $('.openu-speed-menu');
                    var isExpanded = m.attr('aria-expanded') === 'true';
                    
                    // Toggle the aria-expanded attribute
                    m.attr('aria-expanded', !isExpanded);
                    
                    // Toggle the jw-open class
                    $(this).parent()[isExpanded ? 'removeClass' : 'addClass']('jw-open');
                    
                    if (!isExpanded) {
                        $('.openu-speed-content-item.active').focus();
                    }
                });
                
                var text = $('<div/>').addClass('jw-reset jw-tooltip jw-tooltip-speed').append($('<div/>').addClass('jw-text').text($openu.str('speedmenu')));
                var svg = $('<div/>').addClass('jw-reset openu-text-speed');

                btn.on('keyup', function(e) {
                    e.stopImmediatePropagation();
                    if (e.keyCode == 13) {
                        $(this).click();
                        return !1;
                    }
                });

                while (currentSpeed <= maxSpeed) {
                    speeds.push(currentSpeed.toFixed(2));
                    currentSpeed += speedStep;
                }
                speeds.reverse();

                for (var i = 0; i < speeds.length; i++) {
                    var theSpeed = String(speeds[i]);
                    var speed = $('<button/>').on('focusout', function(e) {
                        return setTimeout(function() {
                            if ($('.openu-speed-content-item:hover').length || $('.openu-speed-btn:hover').length)
                                return;
                            if ($('.openu-speed-content-item.focus-within').length)
                                return;
                            if ($('.openu-speed-menu').attr('aria-expanded') !== 'true')
                                return;
                            $('.openu-speed-btn').click();
                            e.preventDefault();
                            return !1;
                        }, 100);
                    }).attr('data-speed', theSpeed).attr('type', "button").attr('aria-checked', "false").attr('role', "menuitemradio").attr('aria-label', $openu.str('speedoption').replace('#', +speeds[i])).addClass('jw-reset openu-speed-content-item').html('x' + speeds[i]);

                    speedList.append(speed);
                    if (speeds[i] == defaultSpeed.toFixed(2)) {
                        speed.addClass('active').attr('aria-checked', "true");
                    }
                }

                svg.html('x' + defaultSpeed.toFixed(2));
                btn.append(svg).append(text).attr('data-content', defaultSpeed.toFixed(2));
                btn.insertBefore($('.jw-icon-settings'));
                speedList.insertBefore($('.openu-text-speed'));

                $('#vod .openu-speed-menu button[role="menuitemradio"]').on("keydown click", function(event) {
                    if (event.type === "click" || (event.type === "keydown" && (event.key === "Enter" || event.key === " "))) {
                        setPlaySpeed($(this).attr('data-speed'));
                        btn.attr('data-content', $(this).attr('data-speed'));
                        speedList.children().removeClass('active');
                        speedList.children().attr('aria-checked', "false");
                        $(this).addClass('active');
                        $(this).attr('aria-checked', "true");
                        $(this).append($('.openu-speed-indicator'));
                        $('.openu-text-speed').html($(this).text());
                        
                        // Close the menu after selection
                        speedList.attr('aria-expanded', 'false');
                        btn.parent().removeClass('jw-open');
                    }
                });
                
                // Handle clicks outside the menu to close it
                $(document).on('click', function(e) {
                    if (!$(e.target).closest('.openu-speed-btn, .openu-speed-menu').length) {
                        speedList.attr('aria-expanded', 'false');
                        btn.parent().removeClass('jw-open');
                    }
                });
                
                // Also close the menu when user interacts with timeline or other controls
                $('.jw-slider-time, .jw-icon-playback').on('click', function() {
                    speedList.attr('aria-expanded', 'false');
                    btn.parent().removeClass('jw-open');
                });
            };

            PlayerSpeedInit();
        };

        // Add notification to show the script is working
        const notification = document.createElement('div');
        notification.style.position = 'fixed';
        notification.style.top = '10px';
        notification.style.right = '10px';
        notification.style.padding = '10px 15px';
        notification.style.background = 'rgba(0, 0, 0, 0.7)';
        notification.style.color = 'white';
        notification.style.borderRadius = '5px';
        notification.style.zIndex = '9999';
        notification.style.fontFamily = 'Arial, sans-serif';
        notification.textContent = 'up to 4x Video Speed Available';
        notification.style.transition = 'opacity 0.5s ease-in-out';

        document.body.appendChild(notification);

        // Make notification disappear after 5 seconds
        setTimeout(() => {
            notification.style.opacity = '0';
            setTimeout(() => notification.remove(), 500);
        }, 5000);

        console.log('Video Speed Enhancer: Successfully replaced AddSpeedControlLogic function');
    }

    // Try to override speed control when DOM is ready
    function init() {
        if (document.readyState === 'complete' || document.readyState === 'interactive') {
            // Check if we're on a page with video
            if (document.querySelector('video') || document.getElementById('vod')) {
                overrideSpeedControl();
            } else {
                // Check for video player initialization
                const observer = new MutationObserver((mutations) => {
                    if (document.querySelector('video') || document.getElementById('vod')) {
                        observer.disconnect();
                        overrideSpeedControl();
                    }
                });

                observer.observe(document.body, { childList: true, subtree: true });

                // Safety timeout - stop observing after 30 seconds
                setTimeout(() => observer.disconnect(), 30000);
            }
        } else {
            document.addEventListener('DOMContentLoaded', init);
        }
    }

    // Start the initialization process
    init();
})();