// ==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 });
})();