// ==UserScript== // @name Weverse Video Rotation // @description Adds buttons and keyboard shortcuts to rotate and flip the video on Weverse.io // @version 1 // @author Slooshi // @namespace Slooshi // @match https://weverse.io/* // @license MIT // @grant GM_addStyle // @downloadURL https://update.greasyfork.icu/scripts/527066/Weverse%20Video%20Rotation.user.js // @updateURL https://update.greasyfork.icu/scripts/527066/Weverse%20Video%20Rotation.meta.js // ==/UserScript== GM_addStyle(` #video-rotation-controls { margin-left: 15px; } #video-rotation-controls button { font-size: 20px; background: #f5f5f5; border: 1px solid #d9d9d9; color: #414141; line-height: 22px; height: 24px; width: 24px; outline: 0; } #video-rotation-controls button:not(:last-child) { margin-right: 3px; } .vwplayer_vlivelive .videoBox { height: calc(100% - 4px); width: calc(100% - 4px); } `); let directions = { left: 'r270', up: 'r0', right: 'r90', down: 'r180', flipH: 'flipH', flipV: 'flipV', } let rotations = { r90: 'rotate(90deg)', r180: 'rotate(180deg)', r270: 'rotate(270deg)', }; let scaling = { flipH: 'scaleX(-1)', flipV: 'scaleY(-1)', portrait: 'scale(1.77777)', landscape: 'scale(0.5625)', }; let attempts = 0; let controlsID = 'video-rotation-controls'; let shortcutsEnabled = true; let shortcuts = { 'Numpad8': directions.up, 'Numpad2': directions.down, 'Numpad4': directions.left, 'Numpad6': directions.right, 'Numpad9': directions.flipH, 'Numpad3': directions.flipV, } let stylesheet = null; let timer = null; (() => { stylesheet = document.createElement('style'); document.head.appendChild(stylesheet); let getAllSubsets = arr => arr.reduce((subsets, value) => subsets.concat(subsets.map(set => [...set, value])), [[]]).slice(1); let scalings = getAllSubsets(Object.keys(scaling)); for (let rule in rotations) { stylesheet.sheet.insertRule(`video.${rule} { transform: ${rotations[rule]}; }`, stylesheet.sheet.cssRules.length); for (let scalingSet of scalings) { stylesheet.sheet.insertRule(`video.${rule}.${scalingSet.reduce((s, v) => s+'.'+v)} { transform: ${rotations[rule] + scalingSet.reduce((s, v) => s+' '+scaling[v], '')}; }`, stylesheet.sheet.cssRules.length); } } let flips = getAllSubsets([directions.flipH, directions.flipV]); for (let flipSet of flips) { stylesheet.sheet.insertRule(`video.${flipSet.reduce((s, v) => s+'.'+v)} { transform:${flipSet.reduce((s, v) => s+' '+scaling[v], '')}; }`, stylesheet.sheet.cssRules.length); } addButtons(); // Re-add buttons if page changes window.history.pushState = function() { History.prototype.pushState.apply(history, arguments); attempts = 0; setTimeout(addButtons, 2000); } })(); function addButtons() { clearTimeout(timer); let btnArea = document.querySelector('.TitleView_title__SSnHb'); if (!btnArea) { if (++attempts < 10) { timer = setTimeout(addButtons, 500); } return; } else if (document.getElementById(controlsID)) { return; } let btnDirections = [ [directions.left, '🠘'], [directions.up, '🠙'], [directions.right, '🠚'], [directions.down, '🠛'], [directions.flipV, '🡙'], [directions.flipH, '🡘'], ]; let div = document.createElement('div'); div.id = controlsID; div.style.marginLeft = '15px'; div.style.display = 'inline-block'; for (let entry of btnDirections) { let btn = document.createElement('button'); btn.dataset.direction = entry[0]; btn.textContent = entry[1]; btn.style.fontSize = '20px'; btn.style.background = '#f5f5f5'; btn.style.border = '1px solid #d9d9d9'; btn.style.color = '#414141'; btn.style.lineHeight = '22px'; btn.style.height = '24px'; btn.style.width = '24px'; btn.style.outline = '0'; btn.style.marginRight = '3px'; div.appendChild(btn); } btnArea.parentNode.insertBefore(div, btnArea.nextSibling); } function rotateVideo(direction) { let video = document.querySelector('video.webplayer-internal-video'); if (!video) return; let flip = [directions.flipH, directions.flipV].includes(direction); if (flip) { video.classList.toggle(direction); } else { video.classList.remove(directions.left, directions.right, directions.down, 'portrait', 'landscape'); if (direction != directions.up) { video.classList.add(direction); if ([directions.left, directions.right].includes(direction)) { video.classList.add(video.videoHeight > video.videoWidth ? 'portrait' : 'landscape'); } } } } window.addEventListener('click', function(e) { if (e.target.parentNode.id != controlsID || e.target.tagName.toLowerCase() != 'button') { return; } else if (e.target.dataset.direction) { rotateVideo(e.target.dataset.direction); } }, true); // Shortcuts window.addEventListener('keydown', function(e) { let stopPropagation = false; if (shortcuts[e.code] && shortcutsEnabled) { rotateVideo(shortcuts[e.code]); stopPropagation = true; } else if (e.code == 'Pause') { shortcutsEnabled = !shortcutsEnabled; stopPropagation = true; } if (stopPropagation) { e.preventDefault(); e.stopPropagation(); } }, true);