Twitch: Twitch Volume Control

Changes the volume of the Twitch player with the mouse wheel and shows the percentage of volume on the screen when the cursor hovers over the video.

// ==UserScript==
// @name         Twitch: Twitch Volume Control
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  Changes the volume of the Twitch player with the mouse wheel and shows the percentage of volume on the screen when the cursor hovers over the video.
// @author       FerNikoMF
// @match        https://www.twitch.tv/*
// @grant        none
// @license      MIT
// @icon         https://i.postimg.cc/4xH3j145/Mediamodifier-Design-Template.png
// ==/UserScript==

(function() {
    'use strict';

    // Function for creating and displaying an overlay with a volume percentage
    function showVolumeOverlay(video, volume) {
        let overlay = document.getElementById('volume-overlay');
        if (!overlay) {
            overlay = document.createElement('div');
            overlay.id = 'volume-overlay';
            // Styles for the overlay
            overlay.style.position = 'fixed';
            overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
            overlay.style.color = 'white';
            overlay.style.padding = '5px 10px';
            overlay.style.borderRadius = '5px';
            overlay.style.fontSize = '16px';
            overlay.style.zIndex = 9999;
            overlay.style.pointerEvents = 'none';
            document.body.appendChild(overlay);
        }
        // Updating the text with the current volume percentage
        overlay.textContent = Math.round(volume * 100) + '%';

        // Calculating the player's position to place the overlay in the center of the video
        const rect = video.getBoundingClientRect();
        overlay.style.left = (rect.left + rect.width / 2) + 'px';
        overlay.style.top = (rect.top + rect.height / 2) + 'px';
        overlay.style.transform = 'translate(-50%, -50%)';
        overlay.style.display = 'block';

        // If the hiding timer is already running, clear it
        if (overlay.hideTimeout) {
            clearTimeout(overlay.hideTimeout);
        }
        // Hiding the overlay after 1 second
        overlay.hideTimeout = setTimeout(() => {
            overlay.style.display = 'none';
        }, 1000);
    }

    // Function for setting a handler for the wheel event
    function addVolumeWheelControl(video) {
        if (!video) return;

        video.addEventListener('wheel', function(event) {
            // Getting the coordinates of the player
            const rect = video.getBoundingClientRect();

            // Check that the cursor is above the video
            if (
                event.clientX < rect.left ||
                event.clientX > rect.right ||
                event.clientY < rect.top ||
                event.clientY > rect.bottom
            ) {
                return;
            }

            // Canceling the standard behavior (for example, scrolling the page)
            event.preventDefault();

            const step = 0.05; // volume change step
            // If the wheel is scrolled down, turn down the volume, otherwise increase
            if (event.deltaY > 0) {
                video.volume = Math.max(video.volume - step, 0);
            } else {
                video.volume = Math.min(video.volume + step, 1);
            }
            // Display the current volume as a percentage
            showVolumeOverlay(video, video.volume);
        }, { passive: false });
    }

    // A function for searching for the <video> tag and installing a handler
    function init() {
        const video = document.querySelector('video');
        if (video) {
            addVolumeWheelControl(video);
        }
    }

    // Initialize immediately, and also monitor changes in the DOM (for example, when loading the player dynamically)
    init();

    // An observer for tracking the addition of new elements (for example, when changing the channel)
    const observer = new MutationObserver(() => {
        const video = document.querySelector('video');
        if (video) {
            addVolumeWheelControl(video);
        }
    });

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