// ==UserScript== // @name Better XVideos.com // @namespace Violentmonkey Scripts // @match https://www.xvideos.com/video* // @grant none // @version 3.0 // @author - // @description Always expand video to large size, scroll down to video, use w a s d to rotate video, q e ` 1 2 3 4 5 z x to seek, v V to frame advance, allow seeking to 0:00, allow clicking anywhere to start video, infinite volume up and down - 2025-05-30, 14:44 // @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js // @downloadURL https://update.greasyfork.org/scripts/428264/Better%20XVideoscom.user.js // @updateURL https://update.greasyfork.org/scripts/428264/Better%20XVideoscom.meta.js // ==/UserScript== this.$ = jQuery.noConflict(true); const scrollToVideo = () => { $("div#video-player-bg")[0].scrollIntoView(false); } // Poll until the video has the desired class const scrollToVideoAfterFullWidth = () => { var host = {}; const scrollPoll = (host) => { if ($("div#content")[0].classList.contains("player-enlarged")) { scrollToVideo(); clearInterval(host.id); setTimeout(scrollToVideo, 500); // one more, for Jesus } } host.id = setInterval(scrollPoll.bind(null, host), 5); } $("div#video-player-bg").ready(() => { setTimeout(() => { if (! $("div#content")[0].classList.contains("player-enlarged")) { $("span.pif-full-width")[0].click(); } scrollToVideoAfterFullWidth(); }, 100); }); // Somehow, the below didn't work - the button got added, but when clicking it, // it showed the menu for the next button to the right (originally from xvideos), // and that button showed the menu for the next one, etc. // //$("button#v-actions-overflow-menu").ready(function () { // var rotationButtons = `<button class="tab-button rotate-video-right"><span>Rotate video right (E)</span></button>` // $(rotationButtons).insertAfter("button#v-actions-overflow-menu") //}) const video = $("div#video-player-bg video")[0]; //keyboard handler for various things $("html").ready(() => { // make whole player clickable to start playing $("div.video-pic").click(() => { $("span.pif-play").trigger("click") }); // define our keyboard handler var orientation = 0; // 0, 90, 180, 270 const setVideoOrientation = (angle) => { // set absolute rotation for video if (0 === angle) { // no rotation $("div.video-bg-pic > video").css("transform", ""); return true; } else if(90 === angle) { // rotated to the right const fitScale = Math.min(video.videoWidth / video.videoHeight, video.videoHeight / video.videoWidth); const transform = `rotate(${angle}deg) scale(${fitScale})`; $("div.video-bg-pic > video").css("transform", transform); return true; } else if (180 === angle) { // upside down $("div.video-bg-pic > video").css("transform", "scaleX(-1) scaleY(-1)"); return true; } else if (270 === angle) { // rotated left const fitScale = Math.min(video.videoWidth / video.videoHeight, video.videoHeight / video.videoWidth); const transform = `rotate(${angle}deg) scale(${fitScale})`; $("div.video-bg-pic > video").css("transform", transform); return true; } return false; } const cycleOrientationRight = () => { const desiredOrientation = (orientation % 360) + 90; const moduloOrientation = desiredOrientation % 360; // make sure it's 0...359 orientation = moduloOrientation; } const cycleOrientationLeft = () => { const desiredOrientation = (orientation % 360) - 90; const moduloOrientation = (desiredOrientation + 360) % 360; // make sure it's 0...359. note x % 360 clamps to (-359.999... ... 359.999) orientation = moduloOrientation; } const cycleOrientation180 = () => { const desiredOrientation = (orientation % 360) + 180; const moduloOrientation = desiredOrientation % 360; // make sure it's 0...359 orientation = moduloOrientation; } // Seek to fraction of video between 0 and 1, meaning start and end. const seekFraction = (fraction) => { if((fraction > 1) || (fraction < 0)) { return false; } video.currentTime = video.duration * fraction; return true; } const videoExtraGain = (mediaElem) => { // Chrome compat var context = new(window.AudioContext || window.webkitAudioContext); var result = { context: context, source: context.createMediaElementSource(mediaElem), gain: context.createGain(), media: mediaElem, amplify: function(multiplier) { result.gain.gain.value = multiplier; }, getAmpLevel: function() { return result.gain.gain.value; } }; result.source.connect(result.gain); result.gain.connect(context.destination); result.amplify(1); return result; } const vidGain = videoExtraGain(video); vidGain.amplify(1); var totalGain = video.volume; const gainToDb = (gain) => 20 * Math.log10(gain); const volumeSet = (desiredGain) => { const dBString = gainToDb(desiredGain).toFixed(1); if (desiredGain <= 1) { vidGain.amplify(1); html5player.setVolume(desiredGain); } else { html5player.setVolume(1); vidGain.amplify(desiredGain); } console.log(`Set gain to ${dBString} dB.`); // logging at the end to make it show up after website spam } const volumeUp = () => { const desiredGain = totalGain * (1/0.9); totalGain