Universal Control Video Player - RafPlayer

Advanced video controller for any website — brightness, playback speed, volume, on-screen shortcut help (H), and fullscreen (F).

// ==UserScript==
// @name         Universal Control Video Player - RafPlayer
// @namespace    http://tampermonkey.net/
// @version      4.1
// @description  Advanced video controller for any website — brightness, playback speed, volume, on-screen shortcut help (H), and fullscreen (F).
// @author       Rafin Saleh
// @license      MIT
// @match        *://*/*
// @grant        none
// ==/UserScript==

/*
Modified for universal use across all websites,
and fullscreen (F) functionality has been re-enabled.
*/

(function () {
    'use strict';

    // --- Display Box ---
    const display = document.createElement('div');
    Object.assign(display.style, {
        position: 'fixed',
        bottom: '10px',
        left: '50%',
        transform: 'translateX(-50%)',
        backgroundColor: 'rgba(0, 0, 0, 0.7)',
        color: 'white',
        padding: '10px 14px',
        borderRadius: '6px',
        zIndex: '999999',
        fontSize: '17px',
        fontFamily: 'sans-serif',
        display: 'none',
        transition: 'opacity 0.3s ease'
    });
    document.body.appendChild(display);

    function showDisplay(msg, duration = 1500) {
        display.textContent = msg;
        display.style.display = 'block';
        display.style.opacity = '1';
        clearTimeout(display.hideTimer);
        display.hideTimer = setTimeout(() => {
            display.style.opacity = '0';
            setTimeout(() => (display.style.display = 'none'), 300);
        }, duration);
    }

    // --- Help Overlay ---
    const helpOverlay = document.createElement('div');
    Object.assign(helpOverlay.style, {
        position: 'fixed',
        top: '50%',
        left: '50%',
        transform: 'translate(-50%, -50%)',
        backgroundColor: 'rgba(0, 0, 0, 0.85)',
        color: 'white',
        padding: '20px',
        borderRadius: '10px',
        zIndex: '1000000',
        fontSize: '15px',
        fontFamily: 'monospace',
        display: 'none',
        whiteSpace: 'pre-line',
        maxWidth: '90%',
        lineHeight: '1.5em'
    });
    helpOverlay.textContent =
`🎥 UDV Universal Controller — Shortcuts (v2.0)

Brightness:
  9 = Increase, 8 = Decrease

Playback Speed:
  Enter = Toggle 1x / 1.75x
  . / , = Fine Adjust (±0.25x), lowest speed is now 1x
  / = Toggle 2.25x / 10x
  A = 3x Speed
  Z = Show Current Speed

Volume:
  I = Increase, K = Decrease (±10%)
  ; = Mute / Unmute

Seek & Frames:
  ← / → = Seek ±5s
  [ / ] = Seek ±60s
  - / = = Frame Back / Forward (when paused)

Misc:
// F = Toggle Fullscreen (DISABLED)
  H = Show / Hide This Help
`;
    document.body.appendChild(helpOverlay);

    let helpVisible = false;
    function toggleHelp() {
        helpVisible = !helpVisible;
        helpOverlay.style.display = helpVisible ? 'block' : 'none';
    }

    // --- States ---
    let brightness = 1;
    let speed = 1.75;
    let volume = 1;
    let muted = false;

    // --- Universal Video Selection ---
    // Selects ALL video elements on the page.
    const videos = () => document.querySelectorAll('video');

    // Selects the video that is likely the main one being controlled (e.g., largest/most visible)
    // For simplicity and universal compatibility, we'll iterate over all active videos.
    function getActiveVideo() {
        // Return the last video found, or the first non-miniplayer video.
        const vids = videos();
        return vids[vids.length - 1]; // Simply control the last discovered video
    }


    // --- Core Functions ---
    function setSpeed(newSpeed) {
        videos().forEach(v => (v.playbackRate = newSpeed));
    }

    function setVolume(newVolume) {
        newVolume = Math.min(1, Math.max(0, newVolume));
        videos().forEach(v => (v.volume = newVolume));
        volume = newVolume;
        showDisplay(`Volume: ${(volume * 100).toFixed(0)}%`);
    }

    function toggleMute() {
        muted = !muted;
        videos().forEach(v => (v.muted = muted));
        showDisplay(muted ? 'Muted' : 'Unmuted');
    }

    function adjustBrightness(change) {
        brightness = Math.min(2, Math.max(0.2, brightness + change));
        document.documentElement.style.filter = `brightness(${brightness})`;
        showDisplay(`Brightness: ${(brightness * 100).toFixed(0)}%`);
    }

    function seek(seconds) {
        const v = getActiveVideo();
        if (v) v.currentTime = Math.min(v.duration, Math.max(0, v.currentTime + seconds));
    }

    function frameStep(forward = true) {
        const v = getActiveVideo();
        if (v && v.paused) {
            // Standard frame rate approximation (e.g., 30 FPS)
            v.currentTime += forward ? 1 / 30 : -1 / 30;
            showDisplay(`Frame ${forward ? '→' : '←'}`);
        }
    }

    // --- Fullscreen (Re-enabled) ---
    function toggleFullscreen() {
        // --- DISABLE: Return immediately to prevent execution ---
        return; 
        // --------------------------------------------------------

        const v = getActiveVideo();
        if (!v) return;

        // Check if the document is currently in fullscreen mode
        if (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement) {
            // Exit fullscreen
            if (document.exitFullscreen) {
                document.exitFullscreen();
            } else if (document.mozCancelFullScreen) { // Firefox
                document.mozCancelFullScreen();
            } else if (document.webkitExitFullscreen) { // Chrome, Safari and Opera
                document.webkitExitFullscreen();
            } else if (document.msExitFullscreen) { // IE/Edge
                document.msExitFullscreen();
            }
            showDisplay('Exited Fullscreen');
        } else {
            // Attempt to enter fullscreen on the video element's container (if available) or the video itself
            const element = v.parentElement || v; 
            
            if (element.requestFullscreen) {
                element.requestFullscreen();
            } else if (element.mozRequestFullScreen) { // Firefox
                element.mozRequestFullScreen();
            } else if (element.webkitRequestFullscreen) { // Chrome, Safari and Opera
                element.webkitRequestFullscreen();
            } else if (element.msRequestFullscreen) { // IE/Edge
                element.msRequestFullscreen();
            }
            showDisplay('Entered Fullscreen');
        }
    }

    // --- Key Controls ---
    window.addEventListener('keydown', e => {
        // Prevent key controls from firing when typing in an input field
        if (['INPUT', 'TEXTAREA', 'SELECT'].includes(document.activeElement.tagName) || document.activeElement.isContentEditable) return;

        // Ensure a video is present and active
        if (!getActiveVideo() && e.key !== 'h') return;

        // Prevent browser default actions for keys like spacebar, arrows, etc.
        // The 'f' key is commented out here to prevent it from calling preventDefault,
        // which helps preserve native browser fullscreen functionality if the element supports it.
        if ([' ', 'ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown', /* 'f', */ 'h', 'Enter'].includes(e.key)) {
            e.preventDefault();
        }

        switch (e.key) {
            case ';': toggleMute(); break;
            case '9': adjustBrightness(0.1); break;
            case '8': adjustBrightness(-0.1); break;
            case 'a': speed = 3; setSpeed(speed); showDisplay(`Speed: 3x`); break;
            case 'z': showDisplay(`Speed: ${speed.toFixed(2)}x`); break;
            case 'Enter': speed = speed === 1.75 ? 1 : 1.75; setSpeed(speed); showDisplay(`Speed: ${speed.toFixed(2)}x`); break;
            case '/': speed = speed === 10 ? 2.25 : 10; setSpeed(speed); showDisplay(`Speed: ${speed.toFixed(2)}x`); break;
            case '[': seek(-60); break;
            case ']': seek(60); break;
            case '-': frameStep(false); break;
            case '=': frameStep(true); break;
            case ',': 
                // MODIFICATION: Set the minimum speed to 1x
                speed = Math.max(1, speed - 0.25); 
                setSpeed(speed); 
                showDisplay(`Speed: ${speed.toFixed(2)}x`); 
                break;
            case '.': speed += 0.25; setSpeed(speed); showDisplay(`Speed: ${speed.toFixed(2)}x`); break;
            case 'i': setVolume(volume + 0.1); break;
            case 'k': setVolume(volume - 0.1); break;
            // case 'ArrowRight': seek(5); break;
            // case 'ArrowLeft': seek(-5); break;
            // case 'f': toggleFullscreen(); break; // DISABLED: Fullscreen function is disabled above
            case 'h': toggleHelp(); break;
            // Removed YouTube-specific quality controls (W/Q)
        }
    });

    // --- Initialization ---
    // Apply initial settings to any video elements that appear
    const initializeVideo = (v) => {
        if (!v.dataset.udvInit) {
            v.dataset.udvInit = '1';
            v.playbackRate = speed;
            v.volume = volume;
            showDisplay('Universal Controller Ready ✅');
        }
    };

    // Initialize all existing videos
    videos().forEach(initializeVideo);

    // Observer to handle videos loaded dynamically (like on YouTube or other single-page apps)
    const observer = new MutationObserver((mutationsList, observer) => {
        videos().forEach(initializeVideo);
    });
    // Monitor the entire document body for changes
    observer.observe(document.body, { childList: true, subtree: true });

})();