NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name XHamster GX // @description Plays all video thumbnails without a mouseover, also cleans up some elements. // @version 0.2 // @author dropdatabase (https://openuserjs.org/users/dropdabase) // @license MIT; https://opensource.org/licenses/MIT // @namespace https://openuserjs.org/users/dropdabase // @match https://xhamster.com/* // @downloadURL https://openuserjs.org/install/dropdabase/XHamster_GX.user.js // ==/UserScript== (function () { 'use strict'; /* V0.2 :: - Convenient button to turn Playing Thumbnails [ON/OFF] - placed in various places depending on the page - Keyboard shortcut [A] will Toggle Playing Thumbnails - Tries not to load all the videos at once, has a 50ms delay between load request - Stores the toggle states on window.localstorage. So it remembers the state you have chosen. V0.1 :: - Plays all thumbnails without a mouseover - Removes the ViewCount and Thumbs Up on the videos, as I think it doesn't offer anything meaningful - Removes some LiveCam thumbnails that are on the videos thumbnails along the normal videos - Removes some Sponsored elements, like the whole TopBar or some banners that can appear - Removes all Live Video banner elements - Changes the padding of the thumbnails a to condense them on the y axis a bit. - Works in most pages (main, video page, user videos) - This is my first public script ------------------------------------------- Notes :: - The placement of the "PLAYTHUMBS" button is not standard, and in some pages it will be missing - openuserjs changed my username from "dropdatabase" to "dropdabase" :-( Future :: - Video thumbnails that are loading will show as black boxex, I can fix this by making the video appear only when it is loaded - Overlay on thumbnails with some info like "watched before"? - Ability to hide videos? */ const REMOVE_TOPVIDEOS = true; // Remove the top bar video section (promoted,recommended) const REMOVE_VIEWS = true; // Remove thumbsUp and viewcount const REMOVE_OTHER = true; // Hide Xhamster Live Banners const TIMECHECK = 90; const LOADDELAY = 40; // Load a video thumbnail every this time // ---------------- var VIDS = null; var VC = null; // ARRAY Holds all the <div> video thumb elements var _ptop = "4px"; // Thumbs new top padding, it looks better this way var L = console.log; // Quicker Log var PG_DYN_PAGER = false; // Does the current page feature a dynamic pager system (video related) var xg_ANIMATE = true; // The global animate switch var btn; var pager = () => { this.timer = null; this.checktxt = ""; this.el = null; // This is used to check if pager is active } var loadv = 0; // = ANIM BUTTON function c_button(name, onclick) { var el = document.createElement('a'); el.innerHTML = name; el.className = 'xh-button'; el.onclick = onclick; this.getEl = () => { return el; } this.set = (st) => { if (st === true) { el.style = "background:#494;color:#222;"; } else { el.style = ""; } } } // ----------- // == START :: // :: Remove some sections if (REMOVE_VIEWS) { removeAll('div.video-thumb-info div.video-thumb-info__metrics'); } if (REMOVE_OTHER) { removeAll('div.wih-spb'); removeAll('div.wih-spot-container'); removeAll('div.clipstore-bottom'); } if (REMOVE_TOPVIDEOS) { removeAll('div.top-video-block'); } // :: Get all the video thumb elements VC = []; document.querySelectorAll('div.video-thumb').forEach((div) => { if (div.classList.contains('wih-cams-thumb-widget')) { L("> Removing Livecam Thumb"); removeEl(div); return; } if (div.classList.contains('video-thumb--with-date')) { // Decorative element removeEl(div.children[0]); L("> Removed Date Added Strip"); } div.style.paddingTop = _ptop; // Alter style VC.push(div); }); // -- end loop if (VC.length == 0) { L("> No Videos on this page"); return; } // -- if (VC[0].parentNode.classList.contains('thumb-list--related')) { L("> DYNAMIC PAGER PAGE"); PG_DYN_PAGER = true; } // Read the saved settings xg_ANIMATE = window.localStorage.getItem("xg_ANIMATE"); if (xg_ANIMATE == null) xg_ANIMATE = true; if (xg_ANIMATE == "true") xg_ANIMATE = true; else xg_ANIMATE = false; L("> Setting Load : xg_ANIMATE", xg_ANIMATE); // -- Setup Keyboard document.onkeypress = function (evt) { evt = evt || window.event; var charCode = evt.keyCode || evt.which; var charStr = String.fromCharCode(charCode); if (charStr == "a") { L("[A] pressed, toggling Thumbnails"); animate_toggle(); } }; createButton(); btn.set(xg_ANIMATE); if (xg_ANIMATE) { start(); } // === END MAIN ===================== function createButton() { // -- UI Buttons btn = new c_button("PlayThumbs", animate_toggle); // : Search elements to figure out where to put the button // Main page let P = document.querySelector('div.categories-container'); if (P) return P.prepend(btn.getEl()); // Video page P = document.querySelector('div.controls'); if (P) return P.append(btn.getEl()); // User video page P = document.querySelector('div.user-content-section'); if (P) return P.prepend(btn.getEl()); } // --------------- function start() { if (PG_DYN_PAGER) { setup_pager(); } process(); } // --------- // -- Prepare for loading function process() { if (VC == null) return; if (VIDS != null) { L("VIDS should be null"); return; } VIDS = []; for (let div of VC) { let a = div.querySelector('a.video-thumb__image-container'); if (!a) { L("[WARNING?] > Thumb Element did not have an <a> tag", div); continue; } // This is for the dynamic pager only if (pager.el == null) { pager.el = a; pager.checktxt = a.href; } // Some VR thumbnails don't have previews if (a.dataset.previewvideo == null) continue; VIDS.push({ video: null, a: a, im: a.querySelector('img'), datav: a.dataset.previewvideo, datas: a.dataset.sprite, timer: -1 }); } // -- endloop loadv = 0; loadVids(); } // ------------------------------------- function loadVids() { // It is guaranteed for VIDS to not be empty // Also for v.previewvideo to be set let d = VIDS[loadv]; let v = document.createElement('video'); d.video = v; v.src = d.datav; v.autoplay = true; v.loop = true; d.a.dataset.previewvideo = ""; d.a.dataset.sprite = ""; if (d.im) d.im.style = "display:none"; d.a.prepend(v); loadv++; if (loadv >= VIDS.length) { L("All Videos Loaded"); return; } d.timer = setTimeout(loadVids, LOADDELAY); } // -- // - Keyboard and Button callback function animate_toggle() { L("ANIMATE TOGGLE"); L(xg_ANIMATE); xg_ANIMATE = !xg_ANIMATE; btn.set(xg_ANIMATE); window.localStorage.setItem('xg_ANIMATE', xg_ANIMATE); if (xg_ANIMATE) { start(); } else { stop(); } L(xg_ANIMATE); } // -------------------------------------- function stop() { L("Stopping , Restoring Thumbnails"); if (pager.timer != null) clearInterval(pager.timer); // Just in case it is running if (VIDS == null) return; for (let v of VIDS) { if (v.im) v.im.style = "display:visible"; // Just in case clearTimeout(v.timer); // Just in case if (!v.video) continue; // it has not loaded yet removeEl(v.video); v.a.dataset.previewvideo = v.datav; v.a.dataset.sprite = v.datas; } // bring image back VIDS = null; } // -------------------------------------- // == Called when an arrow on the pager is clicked // - Remove the Video Elements // - Start a timer to poll for changes // - When new thumbnail infos loaded, create new thumbnails function pager_any_click() { if (xg_ANIMATE == false) return; // Remove old video elements if (VIDS != null) { for (let v of VIDS) { clearTimeout(v.timer); // just in case if (!v.video) continue; // it has not loaded yet removeEl(v.video); } VIDS = null; } // I need to know when the new thumbnails have loaded // So I am polling the HREF data of the first <a> to see when it changes if (pager.el == null) return; // Just in case if (pager.timer != null) return; // No need to set it again, the poll function is the same pager.timer = setInterval(() => { if (pager.el.href != pager.checktxt) { // New data: clearInterval(pager.timer); pager.timer = null; start(); } }, TIMECHECK); } // ------------------------------------- // - Do this BEFORE processing the videos // - Called on : (pagestart,pager refresh) function setup_pager() { let N = document.querySelectorAll('div.pager-container a'); for (let n of N) n.onclick = pager_any_click; pager.el = null; } // ------------------------------------- // ===================================== // == HELPERS // ===================================== function insertAfter(n, ref) { ref.parentNode.insertBefore(n, ref.nextSibling); } // ------------- function removeEl(el) { el.parentNode.removeChild(el); } // ------------- function removeAllIn(el) { while (el.firstChild) { el.removeChild(el.lastChild); } } // ------------- function removeAll(n) { let elems = document.querySelectorAll(n); for (let e of elems) e.parentElement.removeChild(e); } // ------------- })();