Speed Adjustment

Video Speed Adjustment

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

ستحتاج إلى تثبيت إضافة مثل Stylus لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتتمكن من تثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

(لدي بالفعل مثبت أنماط للمستخدم، دعني أقم بتثبيته!)

// ==UserScript==
// @name         Speed Adjustment
// @namespace    http://tampermonkey.net/
// @version      2025-02-27 V1.0.1
// @description  Video Speed Adjustment
// @author       SMK
// @include      *
// @license     MIT
// @icon         https://bkimg.cdn.bcebos.com/pic/d043ad4bd11373f0b4cbf3e2ab0f4bfbfaed04d2?x-bce-process=image/format,f_auto/quality,Q_70/resize,m_lfit,limit_1,w_536
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // 等待页面加载完成
    window.addEventListener('load', function() {
        const targetSelector = '#ad-pic,.ad-pic,#ad-overlay';

        // 隐藏广告元素
        function hideElement() {
            const element = document.querySelector(targetSelector);
            if (element) {
                element.style.display = 'none';
            }
        }

        // 监听页面变化,确保广告及时移除
        const observer = new MutationObserver(hideElement);
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });

        // 初始执行一次
        hideElement();

        // 保存位置到 localStorage
        function savePosition(position) {
            localStorage.setItem('speedControlPosition', JSON.stringify(position));
        }

        // 获取保存的位置,默认为右上角
        function getSavedPosition() {
            const savedPosition = localStorage.getItem('speedControlPosition');
            return savedPosition ? JSON.parse(savedPosition) : { top: '30px', right: '5px' };
        }

        // 选择页面上的所有视频元素
        function addSpeedControlsToVideos() {
            var videos = document.querySelectorAll('video');
            videos.forEach(function(video) {
                if (!video.hasAttribute('data-speed-controls')) { // 防止重复添加
                    var speedContainer = document.createElement('div');
                    speedContainer.style.position = 'absolute';
                    const position = getSavedPosition();
                    speedContainer.style.top = position.top;
                    speedContainer.style.right = position.right;
                    speedContainer.style.zIndex = '1000';
                    speedContainer.style.backgroundColor = 'rgba(0,0,0,0.5)';
                    speedContainer.style.color = 'white';
                    speedContainer.style.padding = '3px';
                    speedContainer.style.borderRadius = '5px';
                    speedContainer.style.display = 'flex';
                    speedContainer.style.flexWrap = 'wrap'; // 使按钮换行
                    speedContainer.style.alignItems = 'center'; // 垂直居中
                    speedContainer.style.overflow = 'hidden'; // 确保容器不会超出范围
                    speedContainer.style.width = '45px'; // 设置容器宽度,以便让按钮能够换行
                    speedContainer.style.transition = 'width 0.3s ease'; // 宽度动画过渡效果
                    speedContainer.style.whiteSpace = 'nowrap'; // 防止按钮换行

                    // 通用按钮样式
                    const buttonStyle = {
                        backgroundColor: 'rgba(0, 0, 0, 0.7)',
                        color: 'white',
                        border: 'none',
                        padding: '3px 6px',
                        borderRadius: '5px',
                        cursor: 'pointer',
                        fontSize: '14px',
                        margin: '3px',
                        fontFamily: 'Arial, sans-serif', // 统一字体
                    };

                    // 创建当前倍速按钮
                    var currentSpeedButton = document.createElement('button');
                    currentSpeedButton.innerText = `${video.playbackRate}x`;
                    Object.assign(currentSpeedButton.style, buttonStyle);

                    // 点击当前倍速按钮时,切换到其他倍速
                    currentSpeedButton.addEventListener('click', function() {
                        const speeds = [0.5, 1.0, 2.0, 2.5, 3.0, 5.0];
                        let currentIndex = speeds.indexOf(video.playbackRate);
                        if (currentIndex === -1) currentIndex = 1; // 默认到1倍速

                        // 循环倍速
                        const nextSpeed = speeds[(currentIndex + 1) % speeds.length];
                        video.playbackRate = nextSpeed;
                        currentSpeedButton.innerText = `${nextSpeed}x`; // 更新按钮显示的倍速
                    });
                    // 添加当前倍速按钮到容器
                    speedContainer.appendChild(currentSpeedButton);

                    // 播放速度选项按钮
                    const speeds = [0.5, 1.0, 2.0, 2.5, 3.0, 5.0];
                    speeds.forEach(function(speed) {
                        var speedButton = document.createElement('button');
                        speedButton.innerText = `${speed}x`;
                        Object.assign(speedButton.style, buttonStyle); // 使用统一的样式

                        // 点击按钮时改变播放速度
                        speedButton.addEventListener('click', function() {
                            const wasPaused = video.paused; // 记录视频是否暂停
                            const wasPlaying = !wasPaused;

                            // 设置播放速度
                            video.playbackRate = speed;
                            currentSpeedButton.innerText = `${speed}x`; // 更新当前倍速按钮显示的倍速

                            // 通过 requestAnimationFrame 确保播放不会中断
                            requestAnimationFrame(() => {
                                if (wasPlaying) {
                                    video.play();
                                }
                            });
                        });

                        // 初始时隐藏其他按钮
                        speedButton.style.display = 'none'; // 关键修改:初始时隐藏
                        speedContainer.appendChild(speedButton); // 将按钮添加到容器中
                    });

                    // 鼠标悬停时展开按钮,鼠标移开时收缩
                    speedContainer.addEventListener('mouseenter', function() {
                        const buttons = speedContainer.querySelectorAll('button');
                        buttons.forEach(button => button.style.display = 'block'); // 显示所有按钮
                        speedContainer.style.width = '270px'; // 扩展容器宽度,显示更多按钮
                    });

                    speedContainer.addEventListener('mouseleave', function() {
                        const buttons = speedContainer.querySelectorAll('button');
                        buttons.forEach((button, index) => {
                            if (index !== 0) button.style.display = 'none'; // 只保留第一个按钮(当前倍速)
                        });
                        speedContainer.style.width = '45px'; // 恢复容器宽度
                    });
                     /**
                    // 创建悬浮窗)

                    **/

                    const floatingWindow = document.createElement('div');
                    floatingWindow.style.position = 'absolute';
                    floatingWindow.style.top = '150px';
                    floatingWindow.style.right = '-10px';
                    floatingWindow.style.zIndex = '1000';
                    floatingWindow.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
                    floatingWindow.style.padding = '15px';
                    floatingWindow.style.borderRadius = '15px';
                    floatingWindow.style.transition = 'opacity 0.3s ease';
                    floatingWindow.style.opacity = '0.3';



                    // 鼠标悬停显示按钮
                    floatingWindow.addEventListener('mouseenter', function() {
                        floatingWindow.style.opacity = '1';
                        positionButtons.forEach(button => button.style.display = 'block');
                    });

                    // 鼠标移出隐藏按钮
                    floatingWindow.addEventListener('mouseleave', function() {
                        floatingWindow.style.opacity = '0.3';
                        positionButtons.forEach(button => button.style.display = 'none');
                    });

                    const positions = [
                        { label: '左上', top: '10px', left: '10px' },
                        { label: '右上', top: '10px', right: '10px' },
                        { label: '左下', bottom: '10px', left: '10px' },
                        { label: '右下', bottom: '10px', right: '10px' }
                    ];

                    const positionButtons = [];
                    positions.forEach(position => {
                        const positionButton = document.createElement('button');
                        positionButton.innerText = position.label;
                        Object.assign(positionButton.style, buttonStyle);
                        positionButton.style.display = 'none';
                        positionButton.style.marginBottom = '5px';

                        positionButton.addEventListener('click', function() {
                            speedContainer.style.top = position.top || '';
                            speedContainer.style.right = position.right || '';
                            speedContainer.style.bottom = position.bottom || '';
                            speedContainer.style.left = position.left || '';
                            savePosition(position);
                        });

                        floatingWindow.appendChild(positionButton);
                        positionButtons.push(positionButton);
                    });



                    // 将按钮容器插入到视频元素之前
                    video.parentNode.insertBefore(speedContainer, video);
                    video.parentNode.insertBefore(floatingWindow, video);
                    video.setAttribute('data-speed-controls', 'true'); // 防止重复添加
                }
            });
        }

        // 创建 MutationObserver 监听 video 元素的变化
        const videoObserver = new MutationObserver(addSpeedControlsToVideos);
        videoObserver.observe(document.body, {
            childList: true,
            subtree: true
        });

        // 初始执行一次
        addSpeedControlsToVideos();
    });
})();