Rumble Volume Control with Mouse Scroll Wheel + Overlay

Change volume on Rumble.com by scrolling over the video and show volume overlay

// ==UserScript==
// @name         Rumble Volume Control with Mouse Scroll Wheel + Overlay
// @namespace    violentmonkey-userscripts
// @version      1.4
// @description  Change volume on Rumble.com by scrolling over the video and show volume overlay
// @match        https://rumble.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    const VOLUME_STEP = 0.05;                // 5% volume change per scroll
    const REVERSE_SCROLL_DIRECTION = true;   // true = normal PC scroll, false = natural/macOS
    const OVERLAY_TIMEOUT = 1500;            // ms before overlay fades out
    const VOLUME_KEY = 'rumble_volume_memory';

    let overlayElement = null;
    let overlayTimeoutId = null;

    function clamp(val, min, max) {
        return Math.min(Math.max(val, min), max);
    }

    function saveVolume(volume) {
        localStorage.setItem(VOLUME_KEY, volume.toString());
    }

    function loadVolume() {
        const v = parseFloat(localStorage.getItem(VOLUME_KEY));
        return isNaN(v) ? 0.5 : clamp(v, 0, 1);
    }

    function createOverlay() {
        if (overlayElement) return;

        overlayElement = document.createElement('div');
        overlayElement.style.position = 'fixed';
        overlayElement.style.top = '110px';
        overlayElement.style.left = '110px';
        overlayElement.style.padding = '5px 10px';
        overlayElement.style.background = 'rgba(0, 0, 0, 0.7)';
        overlayElement.style.color = '#fff';
        overlayElement.style.fontSize = '14px';
        overlayElement.style.borderRadius = '4px';
        overlayElement.style.zIndex = '9999';
        overlayElement.style.transition = 'opacity 0.4s ease';
        overlayElement.style.opacity = '0';
        overlayElement.style.pointerEvents = 'none';
        document.body.appendChild(overlayElement);
    }

    function showOverlay(text) {
        createOverlay();
        overlayElement.textContent = text;
        overlayElement.style.opacity = '1';

        if (overlayTimeoutId) clearTimeout(overlayTimeoutId);
        overlayTimeoutId = setTimeout(() => {
            overlayElement.style.opacity = '0';
        }, OVERLAY_TIMEOUT);
    }

    function onWheelVolumeAdjust(e) {
        if (!e.target || e.target.tagName !== 'VIDEO') return;
        e.preventDefault();

        const video = e.target;
        const delta = e.deltaY * (REVERSE_SCROLL_DIRECTION ? -1 : 1);

        let newVolume = video.volume;
        if (delta > 0) {
            newVolume -= VOLUME_STEP;
        } else if (delta < 0) {
            newVolume += VOLUME_STEP;
        }

        newVolume = clamp(newVolume, 0, 1);

        if (newVolume > 0 && video.muted) {
            video.muted = false;
        }

        video.volume = newVolume;
        video.defaultVolume = newVolume;
        video.dispatchEvent(new Event('volumechange', { bubbles: true }));

        saveVolume(newVolume);
        showOverlay(`Volume: ${Math.round(newVolume * 100)}%`);
    }

    function onMiddleClick(e) {
        if (!e.target || e.target.tagName !== 'VIDEO') return;
        if (e.button !== 1) return; // middle click only
        e.preventDefault();

        const video = e.target;
        video.muted = !video.muted;
        showOverlay(video.muted ? 'Muted' : `Unmuted (${Math.round(video.volume * 100)}%)`);
    }

    function addVolumeControl(video) {
        if (video.dataset.rumbleVolumeAttached) return;

        video.addEventListener('wheel', onWheelVolumeAdjust, { passive: false });
        video.addEventListener('mousedown', onMiddleClick, true);

        const savedVolume = loadVolume();
        video.volume = savedVolume;
        if (savedVolume === 0) {
            video.muted = true;
        }

        video.dataset.rumbleVolumeAttached = 'true';
    }

    function observeVideos() {
        document.querySelectorAll('video').forEach(addVolumeControl);

        const observer = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    if (node.tagName === 'VIDEO') {
                        addVolumeControl(node);
                    } else if (node.querySelectorAll) {
                        node.querySelectorAll('video').forEach(addVolumeControl);
                    }
                }
            }
        });

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

    if (document.readyState === 'complete' || document.readyState === 'interactive') {
        observeVideos();
    } else {
        window.addEventListener('DOMContentLoaded', observeVideos);
    }
})();

QingJ © 2025

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